/****************************************************************************************************

BASIC! is an implementation of the Basic programming language for
Android devices.

Copyright (C) 2010 - 2014 Paul Laughton

This file is part of BASIC! for Android

    BASIC! is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    BASIC! is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with BASIC!.  If not, see <http://www.gnu.org/licenses/>.

    You may contact the author or current maintainers at http://rfobasic.freeforums.org

    Apache Commons Net
    Copyright 2001-2011 The Apache Software Foundation

    This product includes software developed by
    The Apache Software Foundation (http://www.apache.org/).

*************************************************************************************************/

package rfo.mougino.waf;

//Log.v(LOGTAG, CLASSTAG + " Line Buffer  " + ExecutingLineBuffer);

import android.util.DisplayMetrics;
import android.util.Log;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.Flushable;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.BufferedReader;
import java.math.BigDecimal;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.regex.PatternSyntaxException;
import java.util.Iterator; // needed to list activities (mougino)
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.net.DatagramPacket;	// for UDP broadcast server
import java.net.DatagramSocket;	// for UDP broadcast server
import java.net.SocketException;	// for UDP broadcast server

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.ByteArrayBuffer;

import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
//import android.content.ClipData;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Context;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.content.res.Resources.NotFoundException;
//import android.content.ClipboardManager;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.media.SoundPool;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.Vibrator;
import android.os.SystemClock;
import android.speech.RecognizerIntent;
import android.speech.tts.TextToSpeech;
import android.telephony.PhoneStateListener;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.telephony.TelephonyManager;
import android.text.format.Time;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.graphics.Bitmap;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.SensorManager;
import android.text.ClipboardManager;
import android.util.Base64;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;

import org.apache.commons.net.ftp.*;

import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;


/* Executes the Basic program. Run splits into two parts.
 * The UI thread part and the background thread part.
 * 
 * The actual execution of the program occurs in the background
 * task. UI activities must all be handled in the UI task.
 * 
 * This leads to a little complication.
 */

public class Run extends ListActivity {

	public static boolean isOld = false;
	private static final String LOGTAG = "Run";
	private static final String CLASSTAG = Run.class.getSimpleName();
//	Log.v(LOGTAG, CLASSTAG + " Line Buffer  " + ExecutingLineBuffer);

	public static Object LOCK = new Object();

	// ********************* Message types for the Handler *********************
																// message numbers < 256 are in "default" group 0
	private static final int MESSAGE_INPUT_DIALOG      = 1;		// for INPUT command
	private static final int MESSAGE_TOAST             = 2;		// for POPUP command
	private static final int MESSAGE_CONSOLE_WRITE     = 3;		// write to the console
	private static final int MESSAGE_CLEAR_SCREEN      = 4;		// for CLS command
	private static final int MESSAGE_CONSOLE_TITLE     = 5;		// for CONSOLE.TITLE command
	private static final int MESSAGE_CONSOLE_LINE_NEW  = 6;		// for CONSOLE.LINE.NEW command
	private static final int MESSAGE_CONSOLE_LINE_CHAR = 7;		// for CONSOLE.LINE.CHAR command
	private static final int MESSAGE_START_GPS         = 8;		// for GPS.OPEN command
	private static final int MESSAGE_CHECKPOINT        = 9;		// for checkpointMessage() method
	private static final int MESSAGE_PUBLISH_PROGRESS  = 20;

	private static final int MESSAGE_GROUP_MASK        = 0x0F00;// groups can be 0x1 through 0xF

	private static final int MESSAGE_DEFAULT_GROUP     = 0x0000;
	private static final int MESSAGE_BT_GROUP          = 0x0100;// add this offset to messages from BlueTooth commands
	private static final int MESSAGE_HTML_GROUP        = 0x0200;// add this offset to messages from HTML commands

	private static final int MESSAGE_DEBUG_GROUP       = 0x0F00;// add this offset to messages from debug commands

	// ***************************** Command class *****************************

	public static class Command {						// Map a command keyword string to its execution function
		public final String name;						// The command keyword
		public Command(String name) { this.name = name; }
		public boolean run() { return false; }			// Run the command execution function
	}

	private boolean executeCommand(Command[] commands, String type){// If the current line starts with a keyword in a command list
																	// execute the command. The "type" is used only to report errors.
		for (Command c : commands) {								// loop through the command list
			if (ExecutingLineBuffer.startsWith(c.name, LineIndex)) {// if there is a match
				LineIndex += c.name.length();						// move the line index to end of keyword
				return c.run();										// run the function and report back
			}
		}
		RunTimeError("Unknown " + type + " command");
		return false;												// no keyword found
	}

// **********  The variables for the Basic Keywords ****************************    

	public static final String BasicKeyWords[] = {
    	"rem", "dim", "let", 
    	"elseif", 									
    	"endif", "print", "input",
    	"if", "onerror", "else", "end",					// onerror here for Format
    	"for", "include", " ", "next",					// to and step deleted
    	"goto", "gosub", "return",
    	"text.open","text.close","text.readln","text.writeln",
    	"while","repeat","do","until",
    	"onbackkey", 									// onbackkey for Format	
    	"dir", "mkdir", "rename",						// same as file.dir, file.mkdir, file.rename
    	"file.",										// moved all file commands to file_cmd
    	"sql.", " ", "gr.", "pause",
    	"browse", "inkey$", "audio.", 
    	"popup", "sensors.", "gps.",
    	"cls", "array.", "select",
    	"exit", "clipboard.get",
    	"clipboard.put",
    	"encrypt", "decrypt", "split.all",		// split.all new/2013-07-25 gt
    	"split", "byte.open", "byte.close",
    	"byte.read.byte", "byte.write.byte",
    	"graburl", "fn.def", "fn.rtn",
    	"fn.end", "byte.copy",
    	"sw.begin", "sw.case","sw.break",
    	"sw.default", "sw.end", "vibrate",
    	"kb.toggle", "kb.hide", "echo.on",
    	"echo.off", "text.input",
    	" ",  " ",  " ",  " ",	" ",				// moved all file commands to file_cmd
    	"grabfile", "wakelock", "wifilock",
    	"tone", "list.", "bundle.",
    	"stack.", "socket.", "http.post", "device",
    	"debug.", "console.",
    	"tts.","ftp.", "app.",						// moved three "tts" commands to tts_cmd
    	"f_n.continue", "w_r.continue", "d_u.continue",
    	"call", "su.", "system.",
    	"undim", "tget",
    	"f_n.break", "w_r.break", "d_u.break",
    	"text.position.get", "text.position.set",
    	"byte.position.get", "byte.position.set",
    	"byte.read.buffer", "byte.write.buffer",
    	"soundpool.", "myphonenumber", "headset",
    	"sms.send", "phone.call", "email.send",
    	"html.", "run", "@@@", "back.resume",
    	"notify", "swap", "sms.rcv.init",
    	"sms.rcv.next", "stt.listen", "stt.results",
    	"timer.", "timezone.", "time",				// moved three "timer" commands to Timer_cmd
    	" ", "key.resume", "menukey.resume",
    	"onmenukey","ontimer", "onkeypress",			// For Format
    	"ongrtouch", "onbtreadready",						// For Format
    	"home", "background.resume","onbackground",
    	"phone.rcv.init", "phone.rcv.next",
    	"read.data", "read.next", "read.from",
    	"onconsoletouch", "consoletouch.resume"
    	
    };

    private static final int BKWrem  = 0;		// Enumerated names for the keywords
    private static final int BKWdim = 1;
    private static final int BKWlet = 2;
    private static final int BKWelseif = 3;
    private static final int BKWendif = 4;

    private static final int BKWprint = 5;
    private static final int BKWinput = 6;

    private static final int BKWif = 7;
    private static final int BKWonerror = 8;
    private static final int BKWelse = 9;
    private static final int BKWend = 10;

    private static final int BKWfor = 11;
    private static final int BKWinclude = 12;
    private static final int BKWstep = 13;
    private static final int BKWnext = 14;

    private static final int BKWgoto = 15;
    private static final int BKWgosub = 16;
    private static final int BKWreturn = 17;
    private static final int BKWtext_open = 18;
    private static final int BKWtext_close = 19;
    private static final int BKWtext_readln = 20;
    private static final int BKWtext_writeln = 21;
    private static final int BKWwhile = 22;
    private static final int BKWrepeat = 23;
    private static final int BKWdo = 24;
    private static final int BKWuntil = 25;
    private static final int BKWonbreak = 26;
    private static final int BKWdir = 27;
    private static final int BKWmkdir = 28;
    private static final int BKWrename = 29;
    private static final int BKWfile = 30;
    private static final int BKWsql = 31;
    private static final int BKWnull32 = 32;             // Time moved to after Timer
    private static final int BKWgr = 33;
    private static final int BKWpause = 34;
    private static final int BKWbrowse = 35;
    private static final int BKWinkey = 36;
    private static final int BKWaudio = 37;
    private static final int BKWpopup = 38;
    private static final int BKWsensors = 39;
    private static final int BKWgps = 40;
    private static final int BKWcls = 41;
    private static final int BKWarray = 42;
    private static final int BKWselect = 43;
    private static final int BKWexit =44;
    private static final int BKWclipboard_get =45;
    private static final int BKWclipboard_put =46;
    private static final int BKWencrypt =47;
    private static final int BKWdecrypt =48;
    private static final int BKWsplit_all =49;
    private static final int BKWsplit =50;
    private static final int BKWbyte_open =51;
    private static final int BKWbyte_close =52;
    private static final int BKWbyte_read_byte =53;
    private static final int BKWbyte_write_byte =54;
    private static final int BKWgraburl = 55;
    private static final int BKWfn_def = 56;
    private static final int BKWfn_rtn = 57;
    private static final int BKWfn_end = 58;
    private static final int BKWbyte_copy = 59;
    private static final int BKWsw_begin = 60;
    private static final int BKWsw_case = 61;
    private static final int BKWsw_break = 62;
    private static final int BKWsw_default = 63;
    private static final int BKWsw_end = 64;
    private static final int BKWvibrate = 65;
    private static final int BKWkbshow = 66;
    private static final int BKWkbhide = 67;
    private static final int BKWecho_on = 68;
    private static final int BKWecho_off = 69;
    private static final int BKWtext_input = 70;
    private static final int BKWnull71 = 71;				// All file commands moved to file_cmd
    private static final int BKWnull72 = 72;
    private static final int BKWnull73 = 73;
    private static final int BKWnull74 = 74;
    private static final int BKWnull75 = 75;
    private static final int BKWgrabfile = 76;
    private static final int BKWwakelock = 77;
    private static final int BKWwifilock = 78;
    private static final int BKWtone = 79;
    private static final int BKWlist = 80;
    private static final int BKWbundle = 81;
    private static final int BKWstack = 82;
    private static final int BKWsocket = 83;
    private static final int BKWhttp_post = 84;
    private static final int BKWdevice = 85;
    private static final int BKWdebug = 86;
    private static final int BKWconsole = 87;
    private static final int BKWtts = 88;					// TTS commands moved to tts_cmd
    private static final int BKWftp = 89;
    private static final int BKWapp = 90;
    private static final int BKWf_n_continue = 91;
    private static final int BKWw_r_continue = 92;
    private static final int BKWd_u_continue = 93;
    private static final int BKWcall = 94;
    private static final int BKWsu = 95;
    private static final int BKWsystem = 96;
    private static final int BKWundim = 97;
    private static final int BKWtget = 98;
    private static final int BKWf_n_break = 99;
    private static final int BKWw_r_break = 100;
    private static final int BKWd_u_break = 101;
    private static final int BKWtext_position_get = 102;
    private static final int BKWtext_position_set = 103;
    private static final int BKWbyte_position_get = 104;
    private static final int BKWbyte_position_set = 105;
    private static final int BKWbyte_read_buffer = 106;
    private static final int BKWbyte_write_buffer = 107;
    private static final int BKWsound_pool = 108;
    private static final int BKWmy_phone_number = 109;
    private static final int BKWheadset = 110;
    private static final int BKWsms_send = 111;
    private static final int BKWphone_call = 112;
    private static final int BKWemail_send = 113;
    private static final int BKWhtml = 114;
    private static final int BKWrun = 115;
    private static final int BKWempty_program = 116;
    private static final int BKWback_resume = 117;
    private static final int BKWnotify = 118;
    private static final int BKWswap = 119;
    private static final int BKWsms_rcv_init = 120;
    private static final int BKWsms_rcv_next = 121;
    private static final int BKWstt_listen = 122;
    private static final int BKWstt_results = 123;
    private static final int BKWtimer = 124;
    private static final int BKWtimezone = 125;
    private static final int BKWtime = 126;             // Timer commands moved to Timer_cmd
    private static final int BKWnull127 = 127;
    private static final int BKWonkey_resume = 128;
    private static final int BKWmenukey_resume = 129;

    public static final int BKWonmenukey = BKWonerror;			//130    Dummy entries for Format
    public static final int BKWontimer = BKWonerror;			//131
    public static final int BKWonkeypress = BKWonerror;			//132
    public static final int BKWontouch = BKWonerror;			//133
    public static final int BKWonbtreadready = BKWonerror;		//134
    
    private static final int BKWhome = 135;
    private static final int BKWbackground_resume = 136;
    private static final int BKWonbackground = BKWonerror;		//137
    private static final int BKWphone_rcv_init = 138;
    private static final int BKWphone_rcv_next = 139;
    private static final int BKWread_data = 140;
    private static final int BKWread_next = 141;
    private static final int BKWread_from = 142;
    private static final int BKWonconsoletouch = BKWonerror;	//143
    private static final int BKWconsole_resume = 144;

    private static final int BKWnone= 198;
    private static final int SKIP = 199;

    private int KeyWordValue = 0;								// Will contain an enumerated keyword value
    private String PossibleKeyWord = "";						// Used when TO, STEP, THEN are expected

    // **************** The variables for the math function names ************************

    public static final String MathFunctions[] = {
    	"sin(", "cos(", "tan(",
    	"sqr(", "abs(", "rnd(",
    	"val(", "len(", "acos(",
    	"asin(", "atan2(", "ceil(",
    	"floor(", "mod(", "log(",
    	"round(", "toradians(", "todegrees(",
    	"time(", "exp(",
    	"is_in(", "clock(", 
    	"bor(", "band(", "bxor(",
    	"gr_collision(",
    	"ascii(", "starts_with(", "ends_with(",
    	"hex(", "oct(", "bin(", "shift(",
    	"randomize(", "background(",
    	"atan(", "cbrt(", "cosh(", "hypot(",
    	"sinh(", "pow(", "log10(",
    	"ucode(", "pi(", "min(", "max(",		// pi, min, max: new/2013-07-29 gt
    	"int(", "frac(", "sgn(",				// int, frac, sgn: new/2014-03-16 gt
    };

    private static final int MFsin = 0;			// Enumerated name for the Match Functions
    private static final int MFcos = 1;
    private static final int MFtan = 2;
    private static final int MFsqr = 3;
    private static final int MFabs = 4;
    private static final int MFrnd = 5;
    private static final int MFval = 6;
    private static final int MFlen = 7;
    private static final int MFacos = 8;
    private static final int MFasin = 9;
    private static final int MFatan2 = 10;
    private static final int MFceil = 11;
    private static final int MFfloor = 12;
    private static final int MFmod = 13;
    private static final int MFlog = 14;
    private static final int MFround = 15;
    private static final int MFtoradians = 16;
    private static final int MFtodegrees = 17;
    private static final int MFtime = 18;
    private static final int MFexp = 19;
    private static final int MFis_in = 20;
    private static final int MFclock = 21;
    private static final int MFbor = 22;
    private static final int MFband = 23;
    private static final int MFbxor = 24;
    private static final int MFcollision = 25;
    private static final int MFascii = 26;
    private static final int MFstarts_with = 27;
    private static final int MFends_with = 28;
    private static final int MFhex = 29;
    private static final int MFoct = 30;
    private static final int MFbin = 31;
    private static final int MFshift = 32;
    private static final int MFrandomize = 33;
    private static final int MFbackground = 34;
    private static final int MFatan = 35;
    private static final int MFcbrt = 36;
    private static final int MFcosh = 37;
    private static final int MFhypot = 38;
    private static final int MFsinh = 39;
    private static final int MFpow = 40;
    private static final int MFlog10 = 41;
    private static final int MFucode = 42;
    private static final int MFpi = 43;				// 2013-07-29 gt
    private static final int MFmin = 44;			// 2013-07-29 gt
    private static final int MFmax = 45;			// 2013-07-29 gt
    private static final int MFint = 46;			// 2014-03-16 gt
    private static final int MFfrac = 47;			// 2014-03-16 gt
    private static final int MFsgn = 48;			// 2014-03-16 gt

    private int MFNumber = 0;						// Will contain a math function's enumerated name value

	private static final HashMap<String, Integer> mRoundingModeTable = new HashMap<String, Integer>(7) {{
		put("hd", BigDecimal.ROUND_HALF_DOWN);
		put("he", BigDecimal.ROUND_HALF_EVEN);
		put("hu", BigDecimal.ROUND_HALF_UP);
		put("d",  BigDecimal.ROUND_DOWN);
		put("u",  BigDecimal.ROUND_UP);
		put("f",  BigDecimal.ROUND_FLOOR);
		put("c",  BigDecimal.ROUND_CEILING);
	}};

    //*********************** The variables for the Operators  ************************
 
    public static final String OperatorString[]={
    	"<=", "<>", ">=", ">", "<",
    	 "=","^", "*", "+", "-",
    	"/", "!", "|", "&", "(",
    	")", "=", "+", "-"," ",
    	"\n"
    	};

    private static final int LE = 0;					// Enumerated names of operators
    private static final int NE = 1;
    private static final int GE = 2;
    private static final int GT = 3;
    private static final int LT = 4;

    private static final int LEQ = 5;
    private static final int EXP = 6;
    private static final int MUL = 7;
    private static final int PLUS = 8;
    private static final int MINUS = 9;
    
    private static final int DIV = 10;
    private static final int NOT = 11;
    private static final int OR = 12;
    private static final int AND = 13;
    private static final int LPRN = 14;
    
    private static final int RPRN = 15;
    private static final int ASSIGN = 16;
    private static final int UPLUS = 17;
    private static final int UMINUS = 18;

    private static final int SOE = 19;
    private static final int EOL = 20;
    private static final int FLPRN = 21;

    private static final int GoesOnPrecedence[] = {		// Precedence for going onto stack
        8,  8,  8, 8, 8,
        8, 12, 10, 9,  9,
        10, 7,  5,  6, 15,
        2, 15, 13, 13, 
        0, 0, 15,
        15, 15, 15, 15, 15, 15,
        15, 15,
        15, 15, 15, 15,
        15, 15, 15, 15,
        15, 15, 15, 15,
        15, 15, 15 };

    private static final int ComesOffPrecedence[] = {	// Precedence for coming off stack
    	8, 8, 8, 8, 8,
    	8, 12, 10, 9, 9, 
    	10, 7, 5, 6, 2, 
    	14, 1, 13, 13, 
    	0, 0, 2,
        13,13,13,13,13,13,
        13, 13,
        13, 13, 13, 13,
        13, 13, 13, 13,
        13, 13, 13, 13,
        13, 13, 13 };
    
    private int OperatorValue = 0;						// Will hold enumerated operator name value

    //**********************  The variables for the string functions  *******************

    public static final String StringFunctions[] = {
    	"left$(", "mid$(", "right$(",
    	"str$(", "upper$(", "lower$(",
    	"format$(", "chr$(", "version$(",
    	"replace$(", "hex$(", "oct$(",
    	"bin$(", "geterror$(", "word$("
    };
    private int SFNumber = 0;							// Will hold enumerated string function name value

    private static final int SFleft = 0;				// Enumerated string function name values
    private static final int SFmid = 1;
    private static final int SFright = 2;
    private static final int SFstr = 3;
    private static final int SFupper = 4;
    private static final int SFlower = 5;
    private static final int SFformat = 6;
    private static final int SFchr = 7;
    private static final int SFversion = 8;
    private static final int SFreplace = 9;
    private static final int SFhex = 10;
    private static final int SFoct= 11;
    private static final int SFbin =12;
    private static final int SFgeterror =13;
    private static final int SFword =14;

    // *****************************   Various execution control variables *********************

    public static final int BASIC_GENERAL_INTENT = 255;
    private Random randomizer;
    public static boolean background = false;
    private String errorMsg;
    private boolean kbShown = false;

    public static int OnErrorLine = 0;					// Line number for OnError: label
    public static int OnBackKeyLine = 0;
    public static boolean BackKeyHit = false;
    public static int OnMenuKeyLine = 0;
    public static boolean MenuKeyHit = false;
    public static boolean bgStateChange = false;
    private int OnBGLine = 0;
    private int onCTLine = 0;
    private boolean ConsoleTouched = false;
    private boolean ConsoleLongTouch = false;
    private int TouchedConsoleLine = 0;					// first valid line number is 1
    private int interruptResume = -1;
 
    private int LineIndex = 0;							// Current displacement into ExecutingLineBuffer
    private String ExecutingLineBuffer ="";				// Holds the current line being executed
    private int ExecutingLineIndex = 0;					// Points to the current line in Basic.lines
    private boolean SEisLE = false;						// If a String expression result is a logical expression

    private Stack<Integer> GosubStack;					// Stack used for Gosub/Return
    private Stack<Bundle> ForNextStack;					// Stack used for For/Next
    private Stack<Integer> WhileStack;					// Stack used for While/Repeat
    private Stack<Integer> DoStack;						// Stack used for Do/Until

    private Stack <Integer> IfElseStack;				// Stack for IF-ELSE-ENDIF operations
    private static final Integer IEskip1 = 1;				// Skip statements until ELSE, ELSEIF or ENDIF
    private static final Integer IEskip2 = 2;				// Skip to until ENDIF
    private static final Integer IEexec = 3;				// Execute to ELSE, ELSEIF or ENDIF
    private static final Integer IEinterrupt = 4;

    private Double GetNumberValue = (double)0;				// Return value from GetNumber()
    private Double EvalNumericExpressionValue = (double)0;	// Return value from EvalNumericExprssion()
    private Vibrator myVib;

    public static ArrayList<String> output;					// The output screen text lines
    private Basic.ColoredTextAdapter AA;					// The output screen array adapter
    private ListView lv;									// The output screen list view
    private static final int MaxTempOutput = 500;
    private static String TempOutput[] = new String[MaxTempOutput];	// Holds waiting output screen line
    private static int TempOutputIndex = 0;					// Index to next available TempOutput entry

    private Background theBackground;						// Background task ID
    private boolean SyntaxError = false;					// Set true when Syntax Error message has been output

    private boolean ProgressPending = false;
	
	// debugger dialog and ui thread vars
	private static final int MESSAGE_DEBUG_DIALOG = MESSAGE_DEBUG_GROUP + 1;
	private static final int MESSAGE_DEBUG_SWAP   = MESSAGE_DEBUG_GROUP + 2;
	private static final int MESSAGE_DEBUG_SELECT = MESSAGE_DEBUG_GROUP + 3;
	private boolean WaitForResume = false ; 
	private boolean DebuggerStep = false;
	private boolean DebuggerHalt = false;
	private boolean WaitForSwap = false;
	private boolean WaitForSelect = false;
	private boolean dbSwap = false;
	private boolean dbSelect = false;
	private AlertDialog dbDialog;
	private AlertDialog dbSwapDialog;
	private AlertDialog dbSelectDialog;
	private boolean dbDialogScalars;
	private boolean dbDialogArray;
	private boolean dbDialogList;
	private boolean dbDialogStack;
	private boolean dbDialogBundle;
	private boolean dbDialogWatch;
	private boolean dbDialogProgram;
	private boolean dbDialogConsole;
	private String dbConsoleHistory;
	private String dbConsoleExecute;
	private int dbConsoleELBI;
	private ArrayList <Integer> WatchVarIndex;
	private ArrayList <String> Watch_VarNames;
	private int WatchedArray;
	private int WatchedList;
	private int WatchedStack;
	private int WatchedBundle;
	// end debugger ui vars

    public static boolean Stop = false;	   						// Stops program from running
    public static boolean Exit = false;	   						// Exits program and signals caller to exit, too
    public static boolean GraphicsPaused = false;               // Signal from GR that it has been paused
    public static boolean RunPaused = false;                    // Used to control the media player
    public static boolean StopDisplay = false;
    public static boolean DisplayStopped = false;
	private String PrintLine = "";						// Hold the Print line currently being built
	private String textPrintLine = "";					// Hold the TextPrint line currently being built
	private boolean PrintLineReady = false;				// Signals a line is ready to print or write

	private InputMethodManager IMM;
	private HashMap<String,Integer> Labels;				// A list of all labels and associated line numbers

	private ArrayList<String> VarNames ;				// Each entry has the variable name string
	private ArrayList<Integer> VarIndex;				// Each entry is an index into
														// NumberVarValues or
														// StringVarValues or
														// ArrayTable or
														// FunctionTable
	private int VarNumber = 0;							// An index for both VarNames and NVarValue
	public static ArrayList<Double> NumericVarValues;	// if a Var is a number, the VarIndex is an
														// index into this list. The values of numeric
														// array elements are also kept here
	private ArrayList<String> StringVarValues;			// if a Var is a string, the VarIndex is an
														// index into this list. The values of string
														// array elements are also kept here
	private ArrayList<Bundle> ArrayTable;				//Each DIMed array has an entry in this table
	private String StringConstant = "";					// Storage for a string constant
	private int theValueIndex;							// The index into the value table for the current var
	private int ArrayValueStart = 0;					// Value index for newly created array 

	private boolean VarIsFunction = false;				// Flag set by parseVar() when var is a user function
	private ArrayList<Bundle> FunctionTable;			// A bundle is created for each defined function
	private Bundle ufBundle;							// Bundle set by isUserFunction and used by doUserFunction
	private Stack<Bundle> FunctionStack;				// Stack contains the currently executing functions
	private int VarSearchStart;							// Used to limit search for var names to executing function vars
	private int interruptVarSearchStart;				// Save VarSearchStart across interrupt
	private boolean fnRTN = false;						// Set true by fn.rtn. Cause RunLoop() to return

	private boolean VarIsNew = true;					// Signal from get var that this var is new
	private boolean VarIsNumeric = true;				// if false, Var is string
	private boolean VarIsArray = false;					// if true, Var is an Array
														// if the Var is any array, the VarIndex it
														// and index into ArrayTable

	ClipboardManager clipboard;

	// ********************************* File commands *********************************

	public static final String file_KW[] = {
		"delete", "size", "dir", "mkdir",
		"rename", "root", "exists", "type"
	};

	private final Command[] file_cmd = new Command[] {	// Map File command keywords to their execution functions
		new Command("delete")           { public boolean run() { return executeDELETE(); } },
		new Command("size")             { public boolean run() { return executeFILE_SIZE(); } },
		new Command("dir")              { public boolean run() { return executeDIR(); } },
		new Command("mkdir")            { public boolean run() { return executeMKDIR(); } },
		new Command("rename")           { public boolean run() { return executeRENAME(); } },
		new Command("root")             { public boolean run() { return executeFILE_ROOTS(); } },
		new Command("exists")           { public boolean run() { return executeFILE_EXISTS(); } },
		new Command("type")             { public boolean run() { return executeFILE_TYPE(); } }
	};

    // ******************************** Wakelock variables *********************************

    private static PowerManager.WakeLock theWakeLock;
    private static final int partial = 1;
    private static final int dim = 2;
    private static final int bright = 3;
    private static final int full = 4;
    private static final int release = 5;

    // ******************************** Wifilock variables *********************************

    private static WifiManager.WifiLock theWifiLock;
    private static final int wifi_mode_scan = 1;
    private static final int wifi_mode_full = 2;
    private static final int wifi_mode_high = 3;
    private static final int wifi_release = 4;

    // ******************************* File I/O operation variables  **************************

    public static int FMR = 0;			// File Mode Read
    public static int FMW = 1;			// File Mode Write
    public static int FMRW = 2;			// File Mode Read-Write
    
    public static ArrayList<Bundle> FileTable;			// File table list
    public static ArrayList<BufferedReader> BRlist;		// A list of of file readers
    public static ArrayList<FileWriter> FWlist;			// A list of file writers
    public static ArrayList<DataOutputStream> DOSlist;			// A list of file writers
    public static ArrayList<BufferedInputStream> BISlist;			// A list of file writers

	//  ******************  Console Command variables ********************************

	public static final String Console_KW[] = {			// Console commands
		"front", "save", "title",
		"line.count", "line.text", "line.touched",
		"line.new", "line.char"
	};

	private final Command[] Console_cmd = new Command[] {	// Map console command keywords to their execution functions
		new Command("front")            { public boolean run() { return executeCONSOLE_FRONT(); } },
		new Command("save")             { public boolean run() { return executeCONSOLE_DUMP(); } },
		new Command("title")            { public boolean run() { return executeCONSOLE_TITLE(); } },
		new Command("line.count")       { public boolean run() { return executeCONSOLE_LINE_COUNT(); } },
		new Command("line.text")        { public boolean run() { return executeCONSOLE_LINE_TEXT(); } },
		new Command("line.touched")     { public boolean run() { return executeCONSOLE_LINE_TOUCHED(); } },
		new Command("line.new")         { public boolean run() { return executeCONSOLE_LINE_NEW(); } },
		new Command("line.char")        { public boolean run() { return executeCONSOLE_LINE_CHAR(); } }
	};

	//  ******************  Input Command variables ********************************

	private boolean WaitForInput ;							// Signals between background task and foreground
	private boolean BadInput = false;
	private boolean InputCancelled = false;
	private boolean InputIsNumeric = true;
	private int InputVarIndex;								// Index into Numeric or StringVarValues, depending on VarIsNumeric
	private String UserPrompt = "";
	private String InputDefault = "";
	private boolean InputDismissed = false;					// These two will be used only if we dismiss the dialog in onPause
	private AlertDialog theAlertDialog;

    //  ******************  Popup Command variables ********************************
    
    public static String ToastMsg;
    public static int ToastX;
    public static int ToastY;
    public static int ToastDuration;
    
    //  ******************* Variables for the SELECT Command ***********************

    public static int SelectedItem;						// The index of the selected item
    public static boolean ItemSelected;					// Signal from Select.java saying an item has been selected 
    public static boolean SelectLongClick;				// True if long click

    // *********************  SQL Variables  **************************************
    
    public static final String SQL_KW[] = {				// SQL Commands
    	"open", "close", "insert",
    	"query.length", "query.position", "query",
    	"next", "delete", "update", "exec",
    	"raw_query", "drop_table", "new_table"
    };

	private final Command[] SQL_cmd = new Command[] {	// Map SQL command keywords to their execution functions
		new Command("open")             { public boolean run() { return execute_sql_open(); } },
		new Command("close")            { public boolean run() { return execute_sql_close(); } },
		new Command("insert")           { public boolean run() { return execute_sql_insert(); } },
		new Command("query.length")     { public boolean run() { return execute_sql_query_length(); } },
		new Command("query.position")   { public boolean run() { return execute_sql_query_position(); } },
		new Command("query")            { public boolean run() { return execute_sql_query(); } },
		new Command("next")             { public boolean run() { return execute_sql_next(); } },
		new Command("delete")           { public boolean run() { return execute_sql_delete(); } },
		new Command("update")           { public boolean run() { return execute_sql_update(); } },
		new Command("exec")             { public boolean run() { return execute_sql_exec(); } },
		new Command("raw_query")        { public boolean run() { return execute_sql_raw_query(); } },
		new Command("drop_table")       { public boolean run() { return execute_sql_drop_table(); } },
		new Command("new_table")        { public boolean run() { return execute_sql_new_table(); } }
	};

    public static ArrayList<SQLiteDatabase> DataBases; 	 // List of created data bases
    public static ArrayList<Cursor> Cursors; 	 		 // List of created data bases
    
    // ******************************** Variables for the INKEY$ command ***********************
    													
    public static boolean KeyPressed = false;
    public static final String Numbers = "0123456789";    // translations for key codes
    public static final String Chars = "abcdefghijklmnopqrstuvwxyz";
    public static String Buffer = "0123456789";
    public static ArrayList<String> InChar ;
    public static int OnKeyLine;
    
    // ********************************* Variables for text.input command ********************
    
    public static String TextInputString = "";
    public static boolean HaveTextInput;
    
    // ******************************** Graphics Declarations **********************************
    
    public static Intent GRclass;						// Graphics Intent Class
    public static boolean GRopen = false;				// Graphics Open Flag
    public static ArrayList<Bundle> DisplayList;
    public static ArrayList<Integer> RealDisplayList;
    public static ArrayList<Paint> PaintList;
    public static ArrayList<Bitmap> BitmapList;
    public static ArrayList<Float> PixelPoints;
    public static Paint aPaint;
    public static Bitmap aBitmap ;
    public static boolean GRrunning;
    public static boolean GRFront;
    public static int ShowStatusBar;
    public static int GRorientation;
    public static boolean Touched;
    public static double TouchX[] = {0,0,0};
    public static double TouchY[] = {0,0,0};
    public static boolean NewTouch[] = {false, false, false};
    public static int OnTouchLine;
    public static Canvas drawintoCanvas = null;

    
    public static final String GR_KW[] = {
    	"open", "render", "color", "line", "rect",
    	"arc", "circle", "oval", "cls", "hide",
    	 "show", "touch2", "text.draw", "text.size",
    	 "text.align", "text.underline", "text.skew",
    	 "text.bold", "text.strike",
    	 "bitmap.load", "get.position", "rotate.start",
    	 "rotate.end", "modify", "orientation",
    	 "screen.to_bitmap", "close", "bitmap.scale",
    	 "front", "bounded.touch2", "bitmap.size",
    	 "bitmap.delete", "set.pixels", "get.pixel",
    	 "save", "text.width", "scale", "newdl",
    	 "clip", "bitmap.crop", "set.stroke",
    	 "poly", "statusbar.show","touch",
    	 "bounded.touch", "bitmap.save",
    	 "camera.shoot", "screen", "camera.autoshoot",
    	 "camera.manualshoot", "paint.get", "brightness",
    	 "bitmap.create", "bitmap.drawinto.start",
    	 "bitmap.drawinto.end", "bitmap.draw",
    	 "get.bmpixel", "get.value", "set.antialias",
    	 "get.textbounds", "text.typeface", "ongrtouch.resume",
    	 "camera.select", " ", "getdl", "point",			// placeholder for "camera.blindshoot"
    	 "get.type", "get.params"
    };

    public static final int gr_open = 0;				// Graphics Commands enums
    public static final int gr_render = 1;
    public static final int gr_color = 2;
    public static final int gr_line = 3;
    public static final int gr_rect = 4;
    public static final int gr_arc = 5;
    public static final int gr_circle = 6;
    public static final int gr_oval = 7;
    public static final int gr_cls = 8;
    public static final int gr_hide = 9;
    public static final int gr_show = 10;
    public static final int gr_touch2 = 11;
    public static final int gr_text_draw =12;
    public static final int gr_text_size =13;
    public static final int gr_text_align =14;
    public static final int gr_text_underline =15;
    public static final int gr_text_skew =16;
    public static final int gr_text_bold =17;
    public static final int gr_text_strike =18;
    public static final int gr_bitmap_load =19;
    public static final int gr_get_position = 20;
    public static final int gr_rotate_start = 21;
    public static final int gr_rotate_end = 22;
    public static final int gr_modify = 23;
    public static final int gr_orientation = 24;
    public static final int gr_screen_to_bitmap = 25;
    public static final int gr_close = 26;
    public static final int gr_bitmap_scale = 27;
    public static final int gr_front = 28;
    public static final int gr_bound_touch2 = 29;
    public static final int gr_bitmap_size = 30;
    public static final int gr_bitmap_delete = 31;
    public static final int gr_set_pixels = 32;
    public static final int gr_get_pixel = 33;
    public static final int gr_save = 34;
    public static final int gr_text_width = 35;
    public static final int gr_scale = 36;
    public static final int gr_newdl = 37;
    public static final int gr_clip = 38;
    public static final int gr_bitmap_crop = 39;
    public static final int gr_stroke_width = 40;
    public static final int gr_poly = 41;
    public static final int gr_statusbar_show = 42;
    public static final int gr_touch = 43;
    public static final int gr_bound_touch = 44;
    public static final int gr_bitmap_save = 45;
    public static final int gr_camera_shoot = 46;
    public static final int gr_screen = 47;
    public static final int gr_camera_autoshoot = 48;
    public static final int gr_camera_manualshoot = 49;
    public static final int gr_paint_get = 50;
    public static final int gr_brightness =51;
    public static final int gr_bitmap_create =52;
    public static final int gr_bitmap_drawinto_start =53;
    public static final int gr_bitmap_drawinto_end  =54;
    public static final int gr_bitmap_draw = 55;    
    public static final int gr_get_bmpixel = 56;
    public static final int gr_get_value = 57;
    public static final int gr_antialias = 58;
    public static final int gr_get_textbounds = 59;
    public static final int gr_text_typeface = 60;
    public static final int gr_ontouch_resume = 61;
    public static final int gr_camera_select = 62;
    public static final int gr_camera_blindshoot = 63;
    public static final int gr_getdl = 64;
    public static final int gr_point = 65;
    public static final int gr_get_type = 66;
    public static final int gr_get_params = 67;

    public static final int gr_none = 98;

    // ******************************** Variables for Audio commands ****************************

    public static final String Audio_KW[] ={
    	"load", "play", "loop", "stop",
    	"volume", "position.current", "position.seek",
    	"length", "release", "pause",
    	"isdone", "record.start", "record.stop"
    };

    private static final int audio_load = 0;
    private static final int audio_play = 1;
    private static final int audio_loop = 2;
    private static final int audio_stop = 3;
    private static final int audio_volume = 4;
    private static final int audio_pcurrent = 5;
    private static final int audio_pseek = 6;
    private static final int audio_length = 7;
    private static final int audio_release = 8;
    private static final int audio_pause = 9;
    private static final int audio_isdone = 10;
    private static final int audio_record_start = 11;
    private static final int audio_record_stop = 12;
    private static final int audio_none = 98;

    private static MediaPlayer theMP = null;
    private static ArrayList<MediaPlayer> theMPList;
    private static ArrayList<String> theMPNameList;
    private static boolean PlayIsDone;
    private MediaRecorder mRecorder = null;

    // ******************************* Variables for Sensor Commands **********************************

    private SensorActivity theSensors;

    public static final String Sensors_KW[] = {
    	"list","open","read","close", "rotate"
    };

    private static final int sensors_list = 0;
    private static final int sensors_open = 1;
    private static final int sensors_read = 2;
    private static final int sensors_close = 3;
    private static final int sensors_rotate = 4;
    private static final int sensors_none = 98;

    // ***********************  Variables for GPS Commands  ******************************************

    public static final String GPS_KW[] = {
    	"altitude", "latitude", "longitude",
    	"bearing", "accuracy", "speed",
    	"provider", "open", "close", "time"
    };

    private static final int gps_altitude = 0;
    private static final int gps_latitude = 1;
    private static final int gps_longitude = 2;
    private static final int gps_bearing = 3;
    private static final int gps_accuracy = 4;
    private static final int gps_speed = 5;
    private static final int gps_provider = 6;
    private static final int gps_open = 7;
    private static final int gps_close = 8;
    private static final int gps_time = 9;

    private GPS theGPS;

	// ************************* Variables for Array Commands

	public static final String Array_KW[] = {
		"length", "load", "sort",
		"sum", "average", "reverse",
		"shuffle", "min","max", "delete",
		"variance", "std_dev", "copy", "search"
	};

	private static final int array_length = 0;
	private static final int array_load = 1;
	private static final int array_sort = 2;
	private static final int array_sum = 3;
	private static final int array_average = 4;
	private static final int array_reverse = 5;
	private static final int array_shuffle = 6;
	private static final int array_min = 7;
	private static final int array_max = 8;
	private static final int array_delete = 9;
	private static final int array_variance = 10;
	private static final int array_std_dev = 11;
	private static final int array_copy = 12;
	private static final int array_search = 13;
	private static final int array_none =99;

	private static boolean DoAverage;
	private static boolean DoReverse;
	private static boolean DoShuffle;
	private static boolean DoVariance;
	private static boolean DoStdDev;
	private static boolean DoSort;
	private static boolean DoMin;
	private static boolean DoMax;
	private static boolean DoSum;

	private static long sTime;

// ************************************ List command variables *********************************

	public static final String List_KW[] = {
		"create", "add.list", "add.array", "add", "replace", 
		"type","get", "clear", "remove", "insert", "size",
		"contains", "toarray", "search"
	};

	private static final int list_new = 0;
	private static final int list_addlist = 1;
	private static final int list_addarray = 2;
	private static final int list_add = 3;
	private static final int list_set = 4;
	private static final int list_gettype = 5;
	private static final int list_get = 6;
	private static final int list_clear = 7;
	private static final int list_remove = 8;
	private static final int list_insert = 9;
	private static final int list_size = 10;
	private static final int list_contains = 11;
	private static final int list_toarray = 12;
	private static final int list_search = 13;
	private static final int list_none = 99;
	
	private static final int list_is_numeric = 1;
	private static final int list_is_string = 0;

	public static ArrayList <ArrayList> theLists;
	public static ArrayList <Integer> theListsType;  

// ************************************ Bundle Variables ****************************************

	public static final String Bundle_KW[]= {
		"create", "put", "get", "type",
		"keys", "copy", "clear", "contain"
	};

	private static final int bundle_create = 0;
	private static final int bundle_put = 1;
	private static final int bundle_get = 2;
	private static final int bundle_type = 3;
	private static final int bundle_keyset = 4;
	private static final int bundle_copy = 5;
	private static final int bundle_clear = 6;
	private static final int bundle_contain = 7;         // ******* El Condor	

	private static ArrayList <Bundle> theBundles;

// *********************************** Stack Variables **********************************************
	public static final String Stack_KW[]= {
		"create", "push", "pop", "peek",
		"type", "isempty", "clear"
	};

	private static final int stack_create = 0;
	private static final int stack_push = 1;
	private static final int stack_pop = 2;
	private static final int stack_peek = 3;
	private static final int stack_type = 4;
	private static final int stack_isempty = 5;
	private static final int stack_clear = 6;

	private static ArrayList<Stack> theStacks;
	private static ArrayList <Integer> theStacksType; 

	private static final int stack_is_numeric = 1;
	private static final int stack_is_string = 0;

//  ******************************* Socket Variables **************************************************

	public static final String Socket_KW[] = {
		"myip", "client.connect", "client.status",
		"client.read.ready", "client.read.line",
		"client.write.line", "client.write.bytes",
		"client.close", "client.server.ip",
		"client.read.file", "client.write.file",
		"server.create", "server.connect", "server.status",
		"server.read.ready", "server.read.line",
		"server.write.line", "server.write.bytes",
		"server.disconnect", "server.close", "server.client.ip",
		"server.read.file", "server.write.file"
	};

	private final Command[] Socket_cmd = new Command[] {		// Map Socket command keywords to their execution functions
		new Command("client.")          { public boolean run() { return executeSocketClient(); } },
		new Command("server.")          { public boolean run() { return executeSocketServer(); } },
		new Command("myip")             { public boolean run() { return executeMYIP(); } }
	};
                                    
	private final Command[] SocketClient_cmd = new Command[] {	// Map Socket client command keywords to their execution functions
		new Command("connect")          { public boolean run() { return executeCLIENT_CONNECT(); } },
		new Command("status")           { public boolean run() { return executeCLIENT_STATUS(); } },
		new Command("read.ready")       { public boolean run() { return executeCLIENT_READ_READY(); } },
		new Command("read.line")        { public boolean run() { return executeCLIENT_READ_LINE(); } },
		new Command("write.line")       { public boolean run() { return executeCLIENT_WRITE_LINE(); } },
		new Command("write.bytes")      { public boolean run() { return executeCLIENT_WRITE_BYTES(); } },
		new Command("close")            { public boolean run() { return executeCLIENT_CLOSE(); } },
		new Command("server.ip")        { public boolean run() { return executeCLIENT_SERVER_IP(); } },
		new Command("read.file")        { public boolean run() { return executeCLIENT_GETFILE(); } },
		new Command("write.file")       { public boolean run() { return executeCLIENT_PUTFILE(); } }
	};

	private final Command[] SocketServer_cmd = new Command[] {	// Map Socket server command keywords to their execution functions
		new Command("create")           { public boolean run() { return executeSERVER_CREATE(); } },
		new Command("connect")          { public boolean run() { return executeSERVER_ACCEPT(); } },
		new Command("status")           { public boolean run() { return executeSERVER_STATUS(); } },
		new Command("read.ready")       { public boolean run() { return executeSERVER_READ_READY(); } },
		new Command("read.line")        { public boolean run() { return executeSERVER_READ_LINE(); } },
		new Command("write.line")       { public boolean run() { return executeSERVER_WRITE_LINE(); } },
		new Command("write.bytes")      { public boolean run() { return executeSERVER_WRITE_BYTES(); } },
		new Command("disconnect")       { public boolean run() { return executeSERVER_DISCONNECT(); } },
		new Command("close")            { public boolean run() { return executeSERVER_CLOSE(); } },
		new Command("client.ip")        { public boolean run() { return executeSERVER_CLIENT_IP(); } },
		new Command("read.file")        { public boolean run() { return executeSERVER_GETFILE(); } },
		new Command("write.file")       { public boolean run() { return executeSERVER_PUTFILE(); } }
	};

    // Constants that indicate the current connection state
    public static final int STATE_NOT_ENABLED = -1;	// channel is not enabled, or server socket not created
    public static final int STATE_NONE = 0;			// channel is doing nothing (initial state)
    public static final int STATE_LISTENING = 1;	// now listening for incoming connections
    public static final int STATE_CONNECTING = 2;	// now initiating an outgoing connection
    public static final int STATE_CONNECTED = 3;	// now connected to a remote device
    public static final int STATE_READING = 4;		// now reading from a remote device
    public static final int STATE_WRITING = 5;		// now writing to a remote device

    private int clientSocketState;
    private int serverSocketState;

    private Socket theClientSocket ;
    private BufferedReader ClientBufferedReader ;
    private PrintWriter ClientPrintWriter ;
	
    private ServerSocket newSS;
    private SocketConnectThread serverSocketConnectThread; 
    private Socket theServerSocket ;
    private BufferedReader ServerBufferedReader ;
    private PrintWriter ServerPrintWriter ;
	
	// *************************************************** Debug Commands ****************************
	
	public static final String Debug_KW[] = {
		"on","off","print","echo.on",
		"echo.off", "dump.scalars",
		"dump.array", "dump.list",
		"dump.stack", "dump.bundle",
		"watch.clear","watch", "show.scalars",
		"show.array","show.list","show.stack",
		"show.bundle","show.watch","show.program",
		"show","console"
	};
	
	public static final int debug_on = 0;
	public static final int debug_off = 1;
	public static final int debug_print = 2;
	public static final int debug_echo_on = 3;
	public static final int debug_echo_off = 4;
	public static final int debug_dump_scalars = 5;
	public static final int debug_dump_array = 6;
	public static final int debug_dump_list = 7;
	public static final int debug_dump_stack = 8;
	public static final int debug_dump_bundle = 9;
	public static final int debug_watch_clear = 10;
	public static final int debug_watch = 11;
	public static final int debug_show_scalars = 12;
	public static final int debug_show_array = 13;
	public static final int debug_show_list = 14;
	public static final int debug_show_stack = 15;
	public static final int debug_show_bundle = 16;
	public static final int debug_show_watch = 17;
	public static final int debug_show_program = 18;
	public static final int debug_show = 19;
	public static final int debug_console = 20;
	public static final int debug_none = 99;
	
	public static boolean Debug = false;
	public static boolean Echo = false;
	
	// *********************************************** Text to Speech *******************************

	public static final String tts_KW[] = {
		"init", "speak.tofile", "speak", "stop"
	};

	private final Command[] tts_cmd = new Command[] {		// Map TTS command keywords to their execution functions
		new Command("init")             { public boolean run() { return executeTTS_INIT(); } },
		new Command("speak.tofile")     { public boolean run() { return executeTTS_SPEAK_TOFILE(); } },
		new Command("speak")            { public boolean run() { return executeTTS_SPEAK(); } },
		new Command("stop")             { public boolean run() { return executeTTS_STOP(); } }
	};

	public static TextToSpeechActivity theTTS;
	public static boolean ttsInit;
	
	// *********************************************** FTP Client *************************************
	
    public static final String ftp_KW[] = {				// SQL Commands
    	"open", "close", "dir", "cd",
    	"get", "put", "delete", "rmdir",
    	"mkdir", "rename"
    };
    
    public static final int ftp_open = 0;
    public static final int ftp_close = 1;
    public static final int ftp_dir = 2;
    public static final int ftp_cd = 3;
    public static final int ftp_get = 4;
    public static final int ftp_put = 5;
    public static final int ftp_delete = 6;
    public static final int ftp_rmdir = 7;
    public static final int ftp_mkdir = 8;
    public static final int ftp_rename = 9;
    

	public FTPClient mFTPClient = null;
	public String FTPdir = null;
	
	// *********************************************** Camera *****************************************
	
	public static Bitmap CameraBitmap;
	public static boolean CameraDone;
	private int CameraNumber;
	private int NumberOfCameras;			// -1 if we don't know yet

	// ***************************************  Bluetooth  ********************************************
	
    // Message types sent from the BluetoothChatService
    public static final int MESSAGE_STATE_CHANGE = MESSAGE_BT_GROUP + 1;
    public static final int MESSAGE_READ         = MESSAGE_BT_GROUP + 2;
    public static final int MESSAGE_WRITE        = MESSAGE_BT_GROUP + 3;
    public static final int MESSAGE_DEVICE_NAME  = MESSAGE_BT_GROUP + 4;

    // Key names received from the BluetoothChatService
    public static final String DEVICE_NAME = "device_name";
    public static final String TOAST = "toast";

    // Intent request codes
    public static final int REQUEST_CONNECT_DEVICE_SECURE = 1;
    public static final int REQUEST_CONNECT_DEVICE_INSECURE = 2;
    public static final int REQUEST_ENABLE_BT = 3;
    
    public static  UUID MY_UUID_SECURE =
        UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    public static  UUID MY_UUID_INSECURE =
        UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

    
    public static int bt_enabled = 0;
    public static int bt_state;
    public static boolean bt_Secure;

    // Name of the connected device
    public static String mConnectedDeviceName = null;
    // Array adapter for the conversation thread
    public static StringBuffer mOutStringBuffer;
    // Local Bluetooth adapter
    public static BluetoothAdapter mBluetoothAdapter = null;
    // Member object for the chat services
    public static BluetoothChatService mChatService = null;
    //
    public static ArrayList <String> BT_Read_Buffer;
    public static BluetoothDevice btConnectDevice = null;
    public static boolean btReadReady = false;
    public static int OnBTReadLine = 0;

    public static final String app_KW[] = {				// App Commands
    	"pkglist$(", "install", "runlist$(", 
    	"connect", "device.name",
    	"write", "read.ready", "read.bytes",
    	"set.uuid", "listen", "reconnect",
    	"onreadready.resume", "disconnect"
    };
    
    public static final int app_pkglist = 0;
    public static final int app_install = 1;
    public static final int app_runlist = 2;
    public static final int bt_connect = 3;
    public static final int bt_device_name = 4;
    public static final int bt_write = 5;
    public static final int bt_read_ready = 6;
    public static final int bt_read_bytes = 7;
    public static final int bt_set_uuid = 8;
    public static final int bt_listen = 9;
    public static final int bt_reconnect = 10;
    public static final int bt_readready_resume = 11;
    public static final int bt_disconnect = 12;

    /**************************************  Superuser and System  ***************************/

    public static final String su_KW[] = {
    	"open", "write", "read.ready",
    	"read.line", "close"
    	
    };
    public static final String[] System_KW = su_KW;

	private final Command[] SU_cmd = new Command[] {	// Map SU/System command keywords to their execution functions
		new Command("open")             { public boolean run() { return execute_SU_open(); } },
		new Command("write")            { public boolean run() { return execute_SU_write(); } },
		new Command("read.ready")       { public boolean run() { return execute_SU_read_ready(); } },
		new Command("read.line")        { public boolean run() { return execute_SU_read_line(); } },
		new Command("close")            { public boolean run() { return execute_SU_close(); } }
	};

    private boolean isSU = true;						// set true for SU commands, false for System commands
    private DataOutputStream SUoutputStream;
    private DataInputStream SUinputStream;
    private Process SUprocess;
    private ArrayList <String> SU_ReadBuffer;
    private SUReader theSUReader = null;
    
/***************************************  SOUND POOL  ************************************/
    
    public static final String sp_KW[] = {
    	"open", "load","play", "stop",
    	"unload", "pause", "resume", 
    	"release", "setvolume","setpriority",
    	"setloop", "setrate"
    };
    
    public static final int sp_open = 0;
    public static final int sp_load = 1;
    public static final int sp_play = 2;
    public static final int sp_stop = 3;
    public static final int sp_unload = 4;
    public static final int sp_pause = 5;
    public static final int sp_resume = 6;
    public static final int sp_release = 7;
    public static final int sp_setvolume = 8;
    public static final int sp_setpriority = 9;
    public static final int sp_setloop = 10;
    public static final int sp_setrate = 11;
    
    public static SoundPool theSoundPool ;
    
    // **************** Headset Vars **************************************
    
    int headsetState;
    String headsetName;
    int headsetMic;
    
    //******************* html Vars ******************************************
    
    public static final String html_KW[]= {
    	"open", "load.url", "load.string",
    	"get.datalink", "close" , "go.back",
    	"go.forward", "clear.cache", 
    	"clear.history", "post"
    };
    
    public static final int html_open = 0;
    public static final int html_load_url = 1;
    public static final int html_load_string = 2;
    public static final int html_get_datalink = 3;
    public static final int html_close = 4;
    public static final int html_go_back = 5;
    public static final int html_go_forward = 6;
    public static final int html_clear_cache = 7;
    public static final int html_clear_history = 8;
    public static final int html_post = 9;
    
    // Message types for the HTML commands
    private static final int MESSAGE_HTML_OPEN     = MESSAGE_HTML_GROUP + 1;
    private static final int MESSAGE_GO_BACK       = MESSAGE_HTML_GROUP + 2;
    private static final int MESSAGE_GO_FORWARD    = MESSAGE_HTML_GROUP + 3;
    private static final int MESSAGE_CLEAR_CACHE   = MESSAGE_HTML_GROUP + 4;
    private static final int MESSAGE_CLEAR_HISTORY = MESSAGE_HTML_GROUP + 5;
    private static final int MESSAGE_LOAD_URL      = MESSAGE_HTML_GROUP + 6;
    private static final int MESSAGE_LOAD_STRING   = MESSAGE_HTML_GROUP + 7;
    private static final int MESSAGE_POST          = MESSAGE_HTML_GROUP + 8;

    Intent htmlIntent;
    public static ArrayList <String> htmlData_Buffer;
    public boolean htmlOpening;
    public String htmlCmd;

    public static boolean Notified;
   
    //********************* SMS Receive Vars ***********************************

    public static ArrayList <String> smsRcvBuffer;
    
    // ******************** Speech to text Vars ********************************
    
    public static final int  VOICE_RECOGNITION_REQUEST_CODE = 1234;
    public static ArrayList <String> sttResults;
    public static boolean sttListening;
    public static boolean sttDone;
    
    // ******************** Timer Variables *******************************

	public static final String Timer_KW[] = {
		"set", "clear", "resume"
	};

	private final Command[] Timer_cmd = new Command[] {		// Map Timer command keywords to their execution functions
		new Command("set")              { public boolean run() { return executeTIMER_SET(); } },
		new Command("clear")            { public boolean run() { return executeTIMER_CLEAR(); } },
		new Command("resume")           { public boolean run() { return executeTIMER_RESUME(); } }
	};

    public static int OnTimerLine;
    public static Timer theTimer;
    public static boolean timerExpired;
    public static boolean timerStarting;

    // ******************** TimeZone Variables *******************************
    
    public static final String TimeZone_KW[] = {
    	"set", "get", "list"
    };

	private final Command[] TimeZone_cmd = new Command[] {		// Map TimeZone command keywords to their execution functions
		new Command("set")              { public boolean run() { return executeTIMEZONE_SET(); } },
		new Command("get")              { public boolean run() { return executeTIMEZONE_GET(); } },
		new Command("list")             { public boolean run() { return executeTIMEZONE_LIST(); } }
    };

    public String theTimeZone = "";

    //********************** Phone RCV variables *************************
    
	public static int phoneState = 0;
	public static String phoneNumber = "";
	public static boolean phoneRcvInited = false;
	public static TelephonyManager mTM;
	
	// ********************* READ variables *****************************
	
	public static int readNext =0;
	public static ArrayList <Bundle> readData;
	    
    // ****************** Headset Broadcast Receiver ***********************
    
    
 
    public class BroadcastsHandler extends BroadcastReceiver {

    	@Override
    	public void onReceive(Context context, Intent intent) {
    	    if (intent.getAction().equalsIgnoreCase(Intent.ACTION_HEADSET_PLUG)) {
    	        String data = intent.getDataString();
    	        Bundle extraData = intent.getExtras();

    	         headsetState = intent.getIntExtra("state", -1);
    	         headsetName = intent.getStringExtra("name");
    	         headsetMic = intent.getIntExtra("microphone", -1);

    	    }
    	}
    }
    private BroadcastsHandler headsetBroadcastReceiver = null;

/*  ***********************************  Start of Basic's run program code **********************
 * 
 * The code is organized (?) as follows:
 * 
 * The first chunk is the the background task doInBackGround. This  runs in a background thread.
 * It actually controls the running of the program by dispatching each line of code to be executed.
 * 
 * The second chunk is a foreground (UI) thread code that receives user interface messages from
 * doInBackground
 * 
 *  The third chunk is the Run Intent entry code. This code initializes things and starts
 *  running the background task.
 *  
 *  The next chunk is the methods that handle asynchronous events generally created by user
 *  key presses. These methods run in the foreground (UI) task.
 *  
 *  The final chunk and the largest part of Run is code that actually executes user program statements.
 *  This code all runs in the background task.
 *  
 */


    public class Background extends Thread {

    // The execution of the basic program is done by this background Thread. This is done to keep the UI task
    // responsive. This method controls Run as it is running in the background.

        @Override
        public void run() {

        	boolean flag = true;
//    		Basic.Echo = Settings.getEcho(Basic.BasicContext);
        	Echo = false;
        	VarSearchStart = 0;
    		fnRTN = false;
    		setVolumeControlStream(AudioManager.STREAM_MUSIC);

        	if (PreScan()) { 				// The execution starts by scanning the source for labels and read.data
        		
        	ExecutingLineIndex = 0;			// Just in case PreScan ever changes it
        	flag = RunLoop();
        	       	
        	Stop = true;		// If Stop is not already set, set it so that menu code can display the right thing
        	PrintLine = "";     // Clear the Print Line buffer
        	PrintLineReady = false;
        	textPrintLine = "";
        	
        	OnBackKeyLine = 0;
        	
    		  if (OnErrorLine == 0 && !SyntaxError && !Exit){
      			  
    	  		  
    	  		    if (!ForNextStack.empty()){
    	  		    	publishProgress("Program ended with FOR without NEXT");
    	  		    }
    	  		    
    	  		    if (!WhileStack.empty()){
    	  		    	publishProgress("Program ended with WHILE without REPEAT");
    	  		    }
    	  		    
    	  		    if (!DoStack.empty()){
    	  		    	publishProgress("Program ended with DO without UNTIL");
    	  		    }
    	  		    
    	  		  }

         	Basic.theRunContext = null;  // Signals that the background task has stopped
         	cleanUp();
        	}
        	
        	else{              // We get here if PreScan found error or duplicate label
        		for (int i=0; i<TempOutputIndex; ++i){				// if new output lines, the send them
        			publishProgress(TempOutput[i]);					// to UI task
        			}
        		TempOutputIndex = 0;
        	}
        	if (Exit) {
        		finish();
        	}
        }

        private void publishProgress(String... s) {
        	mHandler.obtainMessage(MESSAGE_PUBLISH_PROGRESS, s).sendToTarget();
        }

// The RunLoop() drives the execution of the program. It is called from doInBackground and
// recursively from doUserFunction.
        
        public  boolean RunLoop(){
        	boolean flag = true;
        	do {
        		if (ExecutingLineIndex >= Basic.lines.size())break;
        		ExecutingLineBuffer = Basic.lines.get(ExecutingLineIndex);  // Next program line
//        		Log.v(LOGTAG, "Background.RunLoop: " + ExecutingLineBuffer);
        		LineIndex = 0 ;
        		sTime = SystemClock.uptimeMillis();
 
        		flag = StatementExecuter();							// execute the next statement
        															// returns true if no problems executing statement
        		if (Exit) return flag;								// if Exit skip all other processing
        		
        		if (!flag && (OnErrorLine != 0)){                   // If Error and there is an OnError label
        			ExecutingLineIndex = OnErrorLine;               // Go to the OnError line
        			SyntaxError = false;
        			flag = true;									// and indicate no error
        		} else
        		
        		if (BackKeyHit && OnBackKeyLine != 0) {
        				BackKeyHit = doInterrupt(OnBackKeyLine);
        			
        		} else

        		if (MenuKeyHit && OnMenuKeyLine != 0) {
        				MenuKeyHit = doInterrupt(OnMenuKeyLine);
        			
        		} else

        		if (timerExpired && OnTimerLine != 0) {
        				timerExpired = doInterrupt(OnTimerLine);
        			
        		} else
        		
        		if (KeyPressed && OnKeyLine != 0) {
        				KeyPressed = doInterrupt(OnKeyLine);
         			
        		} else

        		if (NewTouch[2] && OnTouchLine != 0) {							// and is not tracked like NewTouch[0] and NewTouch[1]
        				NewTouch[2] = doInterrupt(OnTouchLine);					// used with onGRtouch.
        			
        		} else
        			
        		if (btReadReady && OnBTReadLine != 0) {
        				btReadReady = doInterrupt(OnBTReadLine);
        			
        		} else
        		
        		if (ConsoleTouched && onCTLine != 0) {
        				ConsoleTouched = doInterrupt(onCTLine);
        			
        		} else

        		if (bgStateChange && OnBGLine != 0) {
        				bgStateChange = doInterrupt(OnBGLine);
        		}
       		
        		for (int i=0; i<TempOutputIndex; ++i){				// if new output lines, the send them
        			publishProgress(TempOutput[i]);					// to UI task
        			}
        		TempOutputIndex = 0;
				
				// Debugger control
				// Michael/paulon0n also defined a @@J for "Alert dialog var not set called". TODO?
				while (WaitForResume) {
					Thread.yield();
					if (DebuggerHalt) {
						publishProgress("Execution halted");
						Stop = true;
					}
					if (DebuggerStep) {
						DebuggerStep = false;
						sendMessage(MESSAGE_DEBUG_DIALOG);			// signal UI to run debugger dialog again
						break;
					}
					if (dbSwap) {
						WaitForSwap = true;
						sendMessage(MESSAGE_DEBUG_SWAP);
						while (WaitForSwap) {
							Thread.yield();
							/*
							if(dbSelect){
								dbSelect = false;
								WaitForSelect = true;
								sendMessage(MESSAGE_DEBUG_SELECT);
								while (WaitForSelect) {
									Thread.yield();
								}
							}
							*/
						}
						dbSwap = false;
						sendMessage(MESSAGE_DEBUG_DIALOG);			// signal UI to run debugger dialog again
					}
				}

				while (WaitForInput && !Stop) {						// if waiting for Input
					try {Thread.sleep(500);}catch(InterruptedException e){}
					if (BadInput) {									// If user input was bad
						publishProgress("@@1");						// tell her and try again
					} else if (InputCancelled) {					// User hit back on Input Dialog
						WaitForInput = false;
						if (OnErrorLine != 0) {
							ExecutingLineIndex = OnErrorLine;
						} else {									// Tell user we are stopping
							publishProgress("Input dialog cancelled", "Execution halted");
							Stop = true;							// and stop executing
						}
					}
				}

        		++ExecutingLineIndex;								// Step to next line

        		if (fnRTN){					// fn_rtn signal. If true make RunLoop() return
        			fnRTN = false;			// to doUserFunction
        			return true;
        		}
        		
    		} while (ExecutingLineIndex < Basic.lines.size() && flag && !Stop) ;  //keep executing statements until end
        	return flag;
        }

        private void checkpointProgress() {		// give Run methods a way to checkpoint publishProgress
            ProgressPending = true;
            publishProgress("@@9");				// signal foreground task to clear ProgressPending
        }

        private boolean doInterrupt(int gotoLine) {
        	if (interruptResume != -1) return true;		// If we are handling an interrupt then do not cancel this one
        	interruptResume = ExecutingLineIndex;		// Set the resume Line Number
        	ExecutingLineIndex = gotoLine;				// Set the goto line number
        	interruptVarSearchStart = VarSearchStart;	// Save current VarSearchStart
        	IfElseStack.push(IEinterrupt);
        	return false;								//Turn off the interrupt
        }

    } // End of Background class

	private Context getContext() {
		return GRFront ? GR.context : this;
	}

	// These sendMessage methods are used by Background to send messages to mHandler.
	// For convenience, there are several combinations of message parameters provided.

	private void sendMessage(int what) {
		mHandler.obtainMessage(what).sendToTarget();
	}

	private void sendMessage(int what, Object obj) {			// Use this to send a String or other Object
		mHandler.obtainMessage(what, obj).sendToTarget();
	}

	private void sendMessage(int what, int arg1, int arg2) {	// Use this to send one or two int arguments
		mHandler.obtainMessage(what, arg1, arg2).sendToTarget();
	}

	// This Handler is in the UI (foreground) Task part of Run.
	// It gets control when the background task sends a Message.

	private final Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what & MESSAGE_GROUP_MASK) {
			case MESSAGE_DEFAULT_GROUP:
				switch (msg.what) {
				case MESSAGE_INPUT_DIALOG:
					if (BadInput) {
						BadInput = false;
						Toaster("Not a number. Try Again.").show();
					}
					theAlertDialog = doInputDialog().show();
					break;
				case MESSAGE_TOAST:
					Toast toast = Toaster(ToastMsg, ToastDuration);
					toast.setGravity(Gravity.CENTER, ToastX, ToastY);
					toast.show();
					break;
				case MESSAGE_CLEAR_SCREEN:
					output.clear();
					break;
				case MESSAGE_CONSOLE_TITLE:
					String title = (String)msg.obj;
					setTitle((title != null) ? title : getResources().getString(R.string.run_name));
					break;
				case MESSAGE_PUBLISH_PROGRESS:
					onProgressUpdate((String[])msg.obj);
					break;
				default:
				}
				break;
			case MESSAGE_BT_GROUP:
				handleBTMessage(msg);								// handle Bluetooth Messages
				break;
			case MESSAGE_HTML_GROUP:
				handleHtmlMessage(msg);								// handle HTML Messages
				break;
			case MESSAGE_DEBUG_GROUP:
				handleDebugMessage(msg);							// handle debug Messages
				break;
			default:												// unrecognized Message
				break;												// ignore it
			}
		}
	};

	protected void onProgressUpdate(String... str) {

		for (int i=0; i<str.length; ++i) {								// Check for signals
			if (str[i].startsWith("@")) {								// Fast check for possible signal
				if (str[i].startsWith("@@1")){      					// Input dialog signal
					if (BadInput) {
						BadInput = false;
						Toaster("Not a number. Try Again.").show();
					}
					theAlertDialog = doInputDialog().show();
				}else if (str[i].startsWith("@@4")){					// Does the toast for popup command
					Toast toast = Toaster(ToastMsg, ToastDuration);
					toast.setGravity(Gravity.CENTER, ToastX, ToastY);
					toast.show();
        		}else if (str[i].startsWith("@@5")){					// Clear Screen Signal
        			output.clear();
        		}else if (str[i].startsWith("@@9")){					// from checkpointProgress
        			ProgressPending = false;							// progress is published, done waiting
        		}else if (str[i].startsWith("@@A")){
        			output.add("");
        		}else if (str[i].startsWith("@@B")){
        			char c = str[i].charAt(3);
        			String s = output.get(output.size()-1) + c;
        			output.set(output.size()-1, s);
        		} else {output.add(str[i]);}		// Not a signal, just write msg to screen.
			} else {output.add(str[i]);}			// Not a signal, just write msg to screen.

			setListAdapter(AA);						// show the output
			lv.setSelection(output.size()-1);		// set last line as the selected line to scroll
//			Log.d(LOGTAG, "onProgressUpdate: print <" + str[i] + ">");
		} // for each string
	} // onProgressUpdate()

	
// ********************* Run Entry Point. Called from Editor or AutoRun *******************

	@Override
	public void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);
		Log.v(LOGTAG, CLASSTAG + " On Create " + ExecutingLineIndex );
		
		if (Basic.lines == null){
			Log.e(LOGTAG, CLASSTAG + ".onCreate: Basic.lines null. Restarting BASIC!.");
			Intent intent = new Intent(getApplicationContext(), Basic.class);
			intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
			startActivity(intent);
			finish();
			return;
		}

//		System.gc();
//		Log.v(LOGTAG, CLASSTAG + " isOld  " + isOld);
		if (isOld){
			if (theWakeLock != null){
				theWakeLock.release();
			}
			if (theWifiLock != null){
				theWifiLock.release();
			}
		}
		theWakeLock = null;
		theWifiLock = null;
		isOld = true;

		InitVars();	
//		Log.v(LOGTAG, CLASSTAG + " On Create 2 " + ExecutingLineIndex );

		myVib = (Vibrator) this.getSystemService(VIBRATOR_SERVICE);
															// Establish the output screen
		AA = new Basic.ColoredTextAdapter(this, output, Settings.getConsoleTypeface(this));
		setListAdapter(AA);
		lv = getListView();
		lv.setTextFilterEnabled(false);
		lv.setSelection(0);
		lv.setBackgroundColor(AA.backgroundColor);
		if (Settings.getLinedConsole(this)) {
			lv.setDivider(new ColorDrawable(AA.lineColor));	// override default from theme, sometimes it's invisible
		} else {
			lv.setDividerHeight(0);							// don't show the divider
		}
	
//		IMM.restartInput(lv);
		executeKBHIDE();
		setVolumeControlStream(AudioManager.STREAM_MUSIC);
		setRequestedOrientation(Settings.getSreenOrientation(this));
		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
	
		headsetBroadcastReceiver = new BroadcastsHandler();
		this.registerReceiver(headsetBroadcastReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
	
		Basic.theRunContext = this;

		// Listeners for Console Touch
	
		lv.setOnItemClickListener(new OnItemClickListener() {
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
				if (onCTLine != 0) {
					TouchedConsoleLine = position + 1;
					ConsoleLongTouch = false; 
					ConsoleTouched = true;
				}
			}
		});
	
		lv.setOnItemLongClickListener(new OnItemLongClickListener() {
			public boolean  onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
				if (onCTLine != 0) {
					TouchedConsoleLine = position + 1;
					ConsoleLongTouch = true; 
					ConsoleTouched = true;
					return true;
				}
				return false;
			}
		});

		theBackground = new Background();						// Start the background task
		theBackground.start();

	}

private void InitVars(){

    OnErrorLine = 0;							// Line number for OnError: label
    OnBackKeyLine = 0;
    BackKeyHit = false;
    OnMenuKeyLine = 0;
    MenuKeyHit = false;
    bgStateChange = false;
    OnBGLine = 0;
    onCTLine = 0;
    ConsoleTouched = false;
    ConsoleLongTouch = false;
    TouchedConsoleLine = 0;						// first valid line number is 1


    errorMsg = "No error";
    
    InChar = new ArrayList<String>();
    KeyPressed = false;
    OnKeyLine = 0;
	
    LineIndex = 0;				// Current displacement into ExecutingLineBuffer
    ExecutingLineBuffer ="\n";		// Holds the current line being executed
    ExecutingLineIndex = 0;		// Points to the current line in Basic.lines
    SEisLE = false;				// If a String expression result is a logical expression

    GosubStack = new Stack<Integer>();			// Stack used for Gosub/Return
    ForNextStack = new	Stack<Bundle>();		// Stack used for For/Next
    WhileStack = new Stack<Integer>() ;			// Stack used for While/Repeat
    DoStack = new Stack<Integer>();				// Stack used for Do/Until
    
    IfElseStack = new Stack <Integer>() ;			// Stack for IF-ELSE-ENDIF operations
    GetNumberValue = (double)0;				// Return value from GetNumber()
    EvalNumericExpressionValue = (double)0;	// Return value from EvalNumericExprssion()

    output = new ArrayList<String>();				// The output screen text lines
//    AA = new ArrayAdapter<String>;				// The output screen array adapter
//    lv = new ListView();							// The output screen list view
   TempOutput = new String[MaxTempOutput];	// Holds waiting output screen line
    TempOutputIndex = 0;					// Index to next available TempOutput entry

    theBackground = null;					// Background task ID
    SyntaxError = false;		        // Set true when Syntax Error message has been output

   ProgressPending = false;
   randomizer = null;
   background = false;
   
  
    // debugger ui vars
   Watch_VarNames = new ArrayList<String>(); // watch list of string names
   WatchVarIndex = new ArrayList<Integer>();// watch list of variable indexes
	dbDialogScalars = false;
	dbDialogArray = false;
	dbDialogList = false;
	dbDialogStack = false;
	dbDialogBundle = false;
	dbDialogWatch = false;
	dbDialogProgram = true;
	dbConsoleHistory = "";
	dbConsoleExecute = "";
	dbConsoleELBI = 0;
	WatchedArray = -1;
	WatchedList =-1;
	WatchedStack =-1;
	WatchedBundle =-1;
	dbSwap = false;
	dbDialog = null;
	dbSwapDialog = null;
	dbSelectDialog = null;

    Stop = false;	   						// Stops program from running
    Exit = false;							// Exits program and signals caller to exit, too
    GraphicsPaused = false;               // Signal from GR that it has been paused
    RunPaused = false;                    // Used to control the media player
   StopDisplay = false;
   GRFront = false;
   DisplayStopped = false;
   ShowStatusBar = 0;
   IMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);

   PrintLine = "";					// Hold the Print line currently being built
    PrintLineReady = false;			// Signals a line is ready to print or write

	Labels = new HashMap<String, Integer>();		// A list of all labels and associated line numbers

    VarNames = new ArrayList<String>() ;			// Each entry has the variable name string
    VarIndex = new ArrayList<Integer>();			// Each entry is an index into
    VarNumber = 0;				// An index for both VarNames and NVarValue
    NumericVarValues = new ArrayList<Double>() ;   // if a Var is a number, the VarIndex is an
   StringVarValues  =new ArrayList<String>() ;    // if a Var is a string, the VarIndex is an
   ArrayTable = new ArrayList<Bundle>() ;			//Each DIMed array has an entry in this table
   StringConstant = "";			// Storage for a string constant
    theValueIndex = 0;				// The index into the value table for the current var
	ArrayValueStart = 0;				// Value index for newly created array 
	
	VarIsFunction = false;							// Flag set by parseVar() when var is a user function
	FunctionTable = new ArrayList<Bundle>();		// A bundle is created for each defined function
	ufBundle = null ;								// Bundle set by isUserFunction and used by doUserFunction
	FunctionStack = new Stack<Bundle>() ;			// Stack contains the currently executing functions
	VarSearchStart = 0;								// Used to limit search for var names to executing function vars
	interruptVarSearchStart = 0;					// Save VarSearchStart across interrupt
	fnRTN = false;									// Set true by fn.rtn. Cause RunLoop() to return
	Debug = false;

    VarIsNew = true;				// Signal from get var that this var is new
    VarIsNumeric = true;			// if false, Var is string(
    VarIsArray = false;			// if true, Var is an Array
     
    FileTable = new ArrayList<Bundle>() ;			// File table list
    BRlist = new ArrayList<BufferedReader>() ;		// A list of of file readers
    FWlist = new ArrayList<FileWriter>() ;			// A list of file writers
    DOSlist = new ArrayList<DataOutputStream> () ;			// A list of file writers
    BISlist = new ArrayList<BufferedInputStream> () ;			// A list of file writers
    
    clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);

	//  ******************  Input Command variables ********************************

	WaitForInput = false;
	BadInput = false;
	InputCancelled = false;
	InputIsNumeric = true;
	InputVarIndex = -1;
	UserPrompt = "";
	InputDefault = "";
	InputDismissed = false;
	theAlertDialog = null;

    //  ******************  Popup Command variables ********************************
    
    ToastMsg = "";
    ToastX = 0 ;
    ToastY = 0;
    ToastDuration = 1;

	// *********************  SQL Variables  **************************************

	DataBases = new ArrayList<SQLiteDatabase>();	// List of created data bases
	Cursors = new ArrayList<Cursor>();				// List of created data bases

	// ********************************* Variables for text.input command ********************

	TextInputString = "";
	HaveTextInput = false;

	// ******************************** Graphics Declarations **********************************

    GRclass = null;									// Graphics Intent Class
    GRopen = false;									// Graphics Open Flag
    DisplayList = new ArrayList<Bundle>() ;
    RealDisplayList = new ArrayList<Integer>() ;
    PaintList = new ArrayList<Paint>();
    BitmapList = new ArrayList<Bitmap>();
    PixelPoints = new ArrayList<Float> ();
    aPaint = new Paint();
	PaintList = new ArrayList<Paint>();
    GRrunning = false;
    OnTouchLine = 0;
    Touched = false;

    // ********************************* Variables for Audio Commands

    theMP = null;
    theMPList = new ArrayList<MediaPlayer>();
    theMPNameList = new ArrayList<String>();
    theMPList.add(null);		// We don't use the [0] element of these Lists
    theMPNameList.add(null);

    // ******************************* Variables for Sensor Commands **********************************

    theSensors = null;

    theGPS = null;
	
	DoAverage = false;
	DoReverse = false;
	DoShuffle = false;
	DoVariance = false;
	DoStdDev = false;
	DoSort = false;
	DoMin = false;
	DoMax = false;
	DoSum = false;
	
	theLists = new ArrayList <ArrayList>();
	ArrayList<ArrayList> aList = new ArrayList <ArrayList>();
	theLists.add(aList);
	
	theListsType = new ArrayList <Integer>();
	theListsType.add(0);
	
	theBundles = new ArrayList<Bundle>();
	Bundle aBundle = new Bundle();
	theBundles.add(aBundle);
	
	theStacks = new ArrayList<Stack>();
	Stack aStack = new Stack();
	theStacks.add(aStack);
	
	theStacksType = new ArrayList<Integer>();
	theStacksType.add(0);
	
	theClientSocket = null ;
	ClientBufferedReader = null;
	ClientPrintWriter = null;
	
	newSS = null;
	serverSocketConnectThread = null;
	theServerSocket = null ;
	ServerBufferedReader = null ;
	ServerPrintWriter = null ;

	clientSocketState = STATE_NONE;
	serverSocketState = STATE_NONE;

	theTTS = null;
	ttsInit = false;
	
	mFTPClient = null;
	FTPdir = null;
	
	CameraBitmap = null;
	CameraDone = true;
	NumberOfCameras = -1;

	mConnectedDeviceName = null;
    mOutStringBuffer = null;
    mBluetoothAdapter = null;
    mChatService = null;
    btReadReady = false;
    interruptResume = -1;
    OnBTReadLine = 0;
    
    SUoutputStream = null;
    SUinputStream = null;
    SUprocess = null;
    SU_ReadBuffer = null;
    theSUReader = null;
    
    theSoundPool = null ;
    
     headsetState = -1;
     headsetName = "NA" ;
     headsetMic = -1;
     
     htmlIntent = null;
     htmlOpening = false;
     
     sttListening = false;
     
     OnTimerLine = 0;
     theTimer = null;
     timerExpired = false;
     
     theTimeZone = "";
     
	 phoneState = 0;
	 phoneNumber = "";
	 phoneRcvInited = false;
	 mTM = null;
	 
	 readNext =0;
	 readData = new ArrayList <Bundle>();


}

public void cleanUp(){
	if (theMP != null){
		try {theMP.stop();} catch (IllegalStateException e){}
		if (theMP != null) theMP.release();
		theMP = null;
	}
	
	  if (theSoundPool != null){
			  theSoundPool.release();
			  theSoundPool = null;
		  }
	  
	if ( Web.aWebView != null) Web.aWebView.webClose();

	if (theTimer != null) {
	   theTimer.cancel();
	   theTimer = null;
	}

	ttsStop();

	myVib.cancel();

	if (theMP != null) {
		theMP.release();
		theMP = null;
	}
	if (theMPList != null) {
		for (MediaPlayer mp : theMPList) {
			if (mp != null) { mp.release(); }
		}
		theMPList = null;
		theMPNameList = null;
	}

	if (theSensors != null) {
		theSensors.stop();
		theSensors = null;
	}

	if (theServerSocket != null){
	try{
		theServerSocket.close();
		}catch (Exception e) {
		}
	theServerSocket = null;
	}

	if (serverSocketConnectThread != null) {
		serverSocketConnectThread.interrupt();
		serverSocketConnectThread = null;
	}

	if (newSS != null){
		try{
			newSS.close();
			}catch (Exception e) {
			}
			newSS = null;
		}

	if (theClientSocket != null){
		try{
			theClientSocket.close();
			}catch (Exception e) {
			}
			theClientSocket = null;
		}

	clientSocketState = STATE_NONE;
	serverSocketState = STATE_NONE;

	execute_audio_record_stop();

	Stop = true;										// make sure the background task stops
	Basic.theRunContext = null;
	GraphicsPaused = false;
	RunPaused = false;
	ProgressPending = false;

	if (theGPS != null) {
		Log.d(LOGTAG, "Stopping GPS from cleanUp");
		theGPS.stop();
		theGPS = null;
	}

	if (!Basic.DoAutoRun && SyntaxError){
		Editor.SyntaxErrorDisplacement = ExecutingLineIndex;
	} else Editor.SyntaxErrorDisplacement = -1;
	
	for (int i = 0; i <BitmapList.size(); ++i ){
		   Bitmap SrcBitMap = BitmapList.get(i);
		   if (SrcBitMap != null)
		      SrcBitMap.recycle();
		   BitmapList.set(i, null);
		}
	BitmapList.clear();

	
		if (mChatService != null) {
				mChatService.stop();
				mChatService = null;
			}

			if ( theSUReader != null ){
				theSUReader.stop();
				theSUReader = null;
			}
			if (SUprocess != null) {
				SUprocess.destroy();
				SUprocess = null;
			}
			
//			InitVars();   // Why not?
			
			finishActivity(BASIC_GENERAL_INTENT);

}


// The following methods run in the foreground. The are called by asynchronous user events
// caused by the user pressing a key.

@Override
public boolean onTouchEvent(MotionEvent event){
	super.onTouchEvent(event);
	int action = event.getAction();  // Get action type
	return false;
}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event)  {						// The user hit a key
		// Log.v(LOGTAG, CLASSTAG + " onKeyDown" + keyCode);
		if (keyCode == KeyEvent.KEYCODE_MENU) {
			if (OnMenuKeyLine != 0) {
				MenuKeyHit = true;
				return true;
			}
			if (Basic.isAPK)			// If menu key hit in APK and not trapped by OnMenuKey									
				return true;			// then tell OS to ignore it
			
			return false;				// Let Android create the Run Menu.
		}

		if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
			if (OnBackKeyLine != 0){
				BackKeyHit = true;
				return true;
			}

			if (Basic.DoAutoRun) Exit = true;	// If AutoRun, back key always means exit
			if (!Stop) {
				Stop = true;				// If running a program, stop it
			}
			else finish();				// else already stopped, return to the Editor
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}
	

	@Override
	public boolean onKeyUp( int keyCode, KeyEvent event)  {
		// Log.v(LOGTAG, CLASSTAG + " onKeyUp" + keyCode);

		if (keyCode == KeyEvent.KEYCODE_MENU) {
			if (OnMenuKeyLine != 0) {
				MenuKeyHit = true;
				return true;
			}
			if (Basic.isAPK)			// If menu key hit in APK and not trapped by OnMenuKey									
				return true;			// then tell OS to ignore it
			
			return false;				// Let Android create the Run Menu.
		}
		
		if (kbShown)
			IMM.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);


		if (keyCode == KeyEvent.KEYCODE_BACK && OnBackKeyLine != 0) return true;
	    
	    
	    char c;
	    String theKey = "@";
	    int n ;
	    if (keyCode >= 7 && keyCode <= 16){
	    	n = keyCode - 7;
	    	c = Numbers.charAt(n);
	    	theKey = Character.toString(c);
	    	
	    }else if (keyCode >=29 && keyCode <= 54){
	    	n = keyCode -29;
	    	c = Chars.charAt(n);
	    	theKey = Character.toString(c);
	    }else if (keyCode == 62){ 
	    	c = ' ';
	    	theKey = Character.toString(c);
	    }else if (keyCode >= 19 && keyCode <= 23){
	    	switch (keyCode) {
	    	case 19: theKey = "up"; break;
	    	case 20: theKey = "down"; break;
	    	case 21: theKey = "left"; break;
	    	case 22: theKey = "right"; break;
	    	case 23: theKey = "go"; break;
	    	}
	    } else {
	    	theKey = "key " + keyCode;
	    }
	    
	    synchronized (this) {
	    	InChar.add(theKey);
	    }
    	KeyPressed = true;

	    return true;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {		// Called when the menu key is pressed.
		super.onCreateOptionsMenu(menu);
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.run, menu);
		MenuItem item = menu.getItem(1);
		if (Basic.isAPK) {								// If APK, menu action is "Exit", not "Editor"
			item.setTitle(getString(R.string.exit));
		}
		item.setEnabled(false);
		return true;
	}

@Override
public boolean onPrepareOptionsMenu(Menu menu) {   // Executed when Menu key is pressed (before onCreateOptionsMenu() above.
	
    super.onPrepareOptionsMenu(menu);
    MenuItem item;
    if (Stop){									   // If program running display with Editor dimmed
 	   item = menu.getItem(0);					   // Other wise dim stop and undim Editor
	   item.setEnabled(false);
    	item = menu.getItem(1);
    	item.setEnabled(true);
    }
    return true;
}

public static void MenuStop() {
	Show("Stopped by user.");							// Tell user and then
	Stop = true;										// signal main loop to stop
	OnBackKeyLine = 0;
}

@Override
public  boolean onOptionsItemSelected(MenuItem item) {  // A menu item is selected
	switch (item.getItemId()) {

	case R.id.stop:										// User wants to stop execution
		MenuStop();
		return true;

	case R.id.editor:									// User pressed Editor
		if (!Basic.DoAutoRun && SyntaxError) {
			Editor.SyntaxErrorDisplacement = ExecutingLineIndex;
		}

		Basic.theRunContext = null;
		if (mChatService != null) {
			mChatService.stop();
			mChatService = null;
		}

		finish();

	}
	return true;
}

@Override
protected void onResume() {
	Log.v(LOGTAG, CLASSTAG + " On Resume " + kbShown);

	  RunPaused = false;
	  background = false;
	  bgStateChange = true;
//	  if (Stop) InitVars();

//	  if (WaitForInput) theAlertDialog = doInputDialog().show(); maybe???
	  super.onResume();

}

@Override
protected void onPause() {	
	// The Android OS wants me to dismiss dialog while paused so I will
	
/*	if (WaitForInput){
		theAlertDialog.dismiss();
		InputDismissed = true;
		}*/
	  
	// If there is a Media Player running, pause it and hope
	// that it works.
	Log.v(LOGTAG, CLASSTAG + " onPause " + kbShown);
	if (kbShown)  IMM.hideSoftInputFromWindow(lv.getWindowToken(), 0);
	
/*	  if (theMP != null){
		  try {theMP.pause();} catch (IllegalStateException e){}
		  }
*/	  
	  RunPaused = true;

	super.onPause();
}

@Override
protected void onStart(){
	Log.v(LOGTAG, CLASSTAG + " On Start");
//	InitVars();
	super.onStart();
}

@Override
protected void onStop(){
	Log.v(LOGTAG, CLASSTAG + " onStop " + kbShown);
	System.gc();
	if (!GRrunning) {
		background = true;
		bgStateChange = true;
//		if (kbShown)  IMM.hideSoftInputFromWindow(lv.getWindowToken(), 0);

	}
	super.onStop();
}

@Override
protected void onRestart(){
	Log.v(LOGTAG, CLASSTAG + " onRestart");

	super.onRestart();

}

@Override
protected void onDestroy(){
	Log.v(LOGTAG, CLASSTAG + " On Destroy");

	if (theSensors != null) {
		theSensors.stop();
		theSensors = null;
	}

	if (theGPS != null) {
		Log.d(LOGTAG, "Stopping GPS from onDestroy");
		theGPS.stop();
		theGPS = null;
	}

	if (theWakeLock != null){
		theWakeLock.release();
		theWakeLock = null;
	}

	if (theWifiLock != null){
		theWifiLock.release();
		theWifiLock = null;
	}

	if (BitmapList != null) {
		for (int i = 0; i <BitmapList.size(); ++i) {
			Bitmap SrcBitMap = BitmapList.get(i);
			if (SrcBitMap != null)
				SrcBitMap.recycle();
			BitmapList.set(i, null);
		}
		BitmapList.clear();
		   System.gc();
	}

	if (headsetBroadcastReceiver != null) {
		unregisterReceiver(headsetBroadcastReceiver);
	}

	super.onDestroy();
}

@Override
public void onLowMemory(){
	Show("Warning: Low Memory");
}

//********************** Everything from here to end runs in the background task ***********************

// The methods starting here are the core code for running a Basic program

private void trimArray(ArrayList Array, int start){
	int last = Array.size()-1;
	int k = last;
	while (k>=start){
		Array.remove(k);
		--k;
	}
}

// Look for a BASIC! word: [_@#\l]?[_@#\l\d]*
private String getWord(String line, int start, boolean stopOnPossibleKeyword) {
	int max = line.length();
	if (start >= max || start < 0) { return ""; }
	int li = start;
	char c = line.charAt(li);
	if ((c >= 'a' && c <= 'z') || c == '_' || c == '@' || c == '#') { // if first character matches
		do {														// there's a word
			if (++li >= max) break; 								// done if no more characters

			if (stopOnPossibleKeyword &&							// caller wants to stop at keyword
				line.startsWith(PossibleKeyWord, li)) { break; }	// THEN, TO, or STEP

			c = line.charAt(li);									// get next character
		}
		while ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||	// and check it, stop if not valid
				c == '_' || c == '@' || c == '#');
	}
	return line.substring(start, li);
}

private void getInterruptLabels() {								// check for interrupt labels
	Integer line;
	line = Labels.get("onerror");        OnErrorLine   = (line == null) ? 0 : line.intValue();
	line = Labels.get("onbackkey");      OnBackKeyLine = (line == null) ? 0 : line.intValue();
	line = Labels.get("onmenukey");      OnMenuKeyLine = (line == null) ? 0 : line.intValue();
	line = Labels.get("ontimer");        OnTimerLine   = (line == null) ? 0 : line.intValue();
	line = Labels.get("onkeypress");     OnKeyLine     = (line == null) ? 0 : line.intValue();
	line = Labels.get("ongrtouch");      OnTouchLine   = (line == null) ? 0 : line.intValue();
	line = Labels.get("onbtreadready");  OnBTReadLine  = (line == null) ? 0 : line.intValue();
	line = Labels.get("onbackground");   OnBGLine      = (line == null) ? 0 : line.intValue();
	line = Labels.get("onconsoletouch"); onCTLine      = (line == null) ? 0 : line.intValue();
}

// Scan the entire program. Find all the labels and read.data statements.
// Note: at present it seems nobody downstream needs to have ExecutingLineIndex set.
private boolean PreScan() {
	for (int LineNumber = 0; LineNumber < Basic.lines.size(); ++LineNumber) {
		String line = Basic.lines.get(LineNumber);					// One line at a time
		
		int li = line.indexOf(":");									// fast check
		if ((li <= 0) && (line.charAt(0) != 'r')) { continue; }		// not label or READ.DATA, next line

		ExecutingLineBuffer = line;									// set global for called functions
		// ExecutingLineIndex = LineNumber;
		String word = getWord(line, 0, false);
		LineIndex = word.length();

		if (isNext(':')) {											// if word really is a label, store it
			if (Labels.put(word, LineNumber) != null) { return RunTimeError("Duplicate label name"); }
			if (!checkEOL())                   { return false; }
		}
		else if (line.startsWith("read.data")) {					// Is not a label. If it is READ.DATA
			LineIndex = 9;											// set LineIndex just past READ.DATA
			if (!executeREAD_DATA())           { return false; }	// parse and store the data list
		}
	}
	getInterruptLabels();
	return true;
}

private static void Show(String str){					// Display a Error message on  output screen. Message will be
														// picked up up in main loop and sent to the UI task.
  if (str.startsWith("@@")){							// If this an interprocess message
	  TempOutput[TempOutputIndex]= str;					// send the message
	  ++TempOutputIndex;
  } else												// Not an interprocess message
	if (OnErrorLine == 0){								// If there is an OnError label, not show the message.
		TempOutput[TempOutputIndex]= str;					
		++TempOutputIndex;
	}
}

private static  void PrintShow(String str){				// Display a PRINT message on output screen. Message will be
	if (TempOutputIndex >= MaxTempOutput) return;
	
	TempOutput[TempOutputIndex]= str;					// picked up up in main loop and sent to the UI task.
	++TempOutputIndex;
}


	private  boolean StatementExecuter(){					// Execute one basic line (statement)

		PossibleKeyWord = "";
		GetKeyWord();												// Get the keyword that may start the line

			if (KeyWordValue == BKWnone) KeyWordValue = BKWlet;    // If no keyword, then assume pseudo LET

			if (!IfElseStack.empty()){					// if inside IF-ELSE-ENDIF
				Integer q = IfElseStack.peek();			// decide if we should skip to ELSE or ENDIF
				if (q == IEskip1) {
						if (KeyWordValue == BKWelse || 
								KeyWordValue == BKWelseif ||
								KeyWordValue == BKWif ||
								KeyWordValue == BKWendif){}
						else{KeyWordValue = SKIP;}
				} else if (q == IEskip2) {
						if (KeyWordValue == BKWendif ||
							KeyWordValue == BKWif ){}
						else {KeyWordValue = SKIP;}
				}
			}
			
			if (KeyWordValue != SKIP)
        		if (Echo)
        			{PrintShow(ExecutingLineBuffer.substring(0, ExecutingLineBuffer.length()-1));}

	        switch (KeyWordValue){						// Execute the keyword
	        
	        	case BKWrem:
	        		break;
	        	case BKWdim:
	        		if (!executeDIM()){SyntaxError();return false;}
	        		break;
	        	case BKWlet:
	        		if (!executeLET()){SyntaxError();return false;}
	        		break;
	        	case BKWelseif:
	        		if (!executeELSEIF()){SyntaxError();return false;}
	        		break;
	        	case BKWend:
	        		if (!executeEND()){SyntaxError();return false;}
	        		break;
	        	case BKWprint:
	        		if (!executePRINT()){SyntaxError();return false;}
	        		break;
	        	case BKWinput:
	        		if (!executeINPUT()){SyntaxError();return false;}
	        		break;
	        	case BKWif:
	        		if (!executeIF()){SyntaxError();return false;}
	        		break;
	        	case BKWonerror:
	        		return true;
	        	case BKWelse:
	        		if (!executeELSE()){SyntaxError();return false;}
	        		break;
	        	case BKWendif:
	        		if (!executeENDIF()){SyntaxError();return false;}
	        		break;
	        	case BKWfor:
	        		if (!executeFOR()){SyntaxError();return false;}
	        		break;
	        	case BKWinclude:
	        		break;
	        	case BKWstep:
	        		break;
	        	case BKWnext:
	        		if (!executeNEXT()){SyntaxError();return false;}
	        		break;
	        	case BKWgoto:
	        		if (!executeGOTO()){SyntaxError();return false;}
	        		break;
	        	case BKWgosub:
	        		if (!executeGOSUB()){SyntaxError();return false;}
	        		break;
	        	case BKWreturn:
	        		if (!checkEOL()) return false;
	        		if (GosubStack.empty()){
	        			RunTimeError("RETURN without GOSUB");
	        			return false;
	        		}
	        		ExecutingLineIndex = GosubStack.pop();
	        		break;
	        	case BKWtext_open:
	        		if (!executeTEXT_OPEN()){SyntaxError(); return false;}
	        		break;
	        	case BKWtext_close:
	        		if (!executeTEXT_CLOSE()){SyntaxError(); return false;}
	        		break;
	        	case BKWtext_readln:
	        		if (!executeTEXT_READLN()){SyntaxError(); return false;}
	        		break;
	        	case BKWtext_writeln:
	        		if (!executeTEXT_WRITELN()){SyntaxError(); return false;}
	        		break;
	        	case BKWwhile:
	        		if (!executeWHILE()){SyntaxError(); return false;}
	        		break;
	        	case BKWrepeat:
	        		if (!executeREPEAT()){SyntaxError(); return false;}
	        		break;
	        	case BKWdo:
	        		if (!executeDO()){SyntaxError(); return false;}
	        		break;
	        	case BKWuntil:
	        		if (!executeUNTIL()){SyntaxError(); return false;}
	        		break;
	        	case BKWonbreak:
	        		return true;
	        	case BKWdir:
	        		if (!executeDIR()){SyntaxError(); return false;}
	        		break;
	        	case BKWmkdir:
	        		if (!executeMKDIR()){SyntaxError(); return false;}
	        		break;
	        	case BKWrename:
	        		if (!executeRENAME()){SyntaxError(); return false;}
	        		break;
	        	case BKWfile:
	        		if (!executeFILE()){SyntaxError(); return false;}
	        		break;
	        	case BKWsql:
	        		if (!executeSQL()){SyntaxError(); return false;}
	        		break;
	        	case BKWtime:
	        		if (!executeTIME()){SyntaxError(); return false;}
	        		break;
	        	case BKWgr:
	        		if (!executeGR()){SyntaxError(); return false;}
	        		break;
	        	case BKWpause:
	        		if (!executePAUSE()){SyntaxError(); return false;}
	        		break;
	        	case BKWbrowse:
	        		if (!executeBROWSE()){SyntaxError(); return false;}
	        		break;
	        	case BKWinkey:
	        		if (!executeINKEY()){SyntaxError(); return false;}
	        		break;
	        	case BKWaudio:
	        		if (!executeAUDIO()){SyntaxError(); return false;}
	        		break;
	        	case BKWpopup:
	        		if (!executePOPUP()){SyntaxError(); return false;}
	        		break;
	        	case BKWsensors:
	        		if (!executeSENSORS()){SyntaxError(); return false;}
	        		break;
	        	case BKWgps:
	        		if (!executeGPS()){SyntaxError(); return false;}
	        		break;
	        	case BKWcls:
	        		if (!executeCLS()){SyntaxError(); return false;}
	        		break;
	        	case BKWarray:
	        		if (!executeARRAY()){SyntaxError(); return false;}
	        		break;
	        	case BKWselect:
	        		if (!executeSELECT()){SyntaxError(); return false;}
	        		break;
	        	case BKWexit:
	        		Stop = Exit = true;
	                break;
	        	case BKWclipboard_get:
	        		if (!executeCLIPBOARD_GET()){SyntaxError(); return false;}
	        		break;
	        	case BKWclipboard_put:
	        		if (!executeCLIPBOARD_PUT()){SyntaxError(); return false;}
	        		break;
	        	case BKWencrypt:
	        		if (!executeENCRYPT()){SyntaxError(); return false;}
	        		break;
	        	case BKWdecrypt:
	        		if (!executeDECRYPT()){SyntaxError(); return false;}
	        		break;
	        	case BKWsplit:
	        		if (!executeSPLIT(0)){SyntaxError(); return false;}
	        		break;
	        	case BKWsplit_all:		// split.all new/2013-07-25 gt
	        		if (!executeSPLIT(-1)){SyntaxError(); return false;}
	        		break;
	        	case BKWundim:
	        		if (!executeUNDIM()){SyntaxError(); return false;}
	        		break;
	        	case BKWbyte_open:
	        		if (!executeBYTE_OPEN()){SyntaxError(); return false;}
	        		break;
	        	case BKWbyte_close:
	        		if (!executeBYTE_CLOSE()){SyntaxError(); return false;}
	        		break;
	        	case BKWbyte_read_byte:
	        		if (!executeBYTE_READ_BYTE()){SyntaxError(); return false;}
	        		break;
	        	case BKWbyte_write_byte:
	        		if (!executeBYTE_WRITE_BYTE()){SyntaxError(); return false;}
	        		break;
	        	case BKWgraburl:
	        		if (!executeGRABURL()){SyntaxError(); return false;}
	        		break;
	        	case BKWfn_def:
	        		if (!executeFN_DEF()){SyntaxError(); return false;}
	        		break;
	        	case BKWfn_rtn:
	        		if (!executeFN_RTN()){SyntaxError(); return false;}
	        		break;
	        	case BKWfn_end:
	        		if (!executeFN_END()){SyntaxError(); return false;}
	        		break;
	        	case BKWbyte_copy:
	        		if (!executeBYTE_COPY()){SyntaxError(); return false;}
	        		break;
	        	case BKWsw_begin:
	        		if (!executeSW_BEGIN()){SyntaxError(); return false;}
	        		break;
	        	case BKWsw_case:
	        		if (!executeSW_CASE()){SyntaxError(); return false;}
	        		break;
	        	case BKWsw_break:
	        		if (!executeSW_BREAK()){SyntaxError(); return false;}
	        		break;
	        	case BKWsw_default:
	        		if (!executeSW_DEFAULT()){SyntaxError(); return false;}
	        		break;
	        	case BKWsw_end:
	        		if (!executeSW_END()){SyntaxError(); return false;}
	        		break;
	        	case BKWvibrate:
	        		if (!executeVIBRATE()){SyntaxError(); return false;}
	        		break;
	        	case BKWkbshow:
	        		if (!executeKBSHOW()){SyntaxError(); return false;}
	        		break;
	        	case BKWkbhide:
	        		if (!executeKBHIDE()){SyntaxError(); return false;}
	        		break;
	        	case BKWecho_on:
	        		if (!executeECHO_ON()){SyntaxError(); return false;}
	        		break;
	        	case BKWecho_off:
	        		if (!executeECHO_OFF()){SyntaxError(); return false;}
	        		break;
	        	case BKWtext_input:
	        		if (!executeTEXT_INPUT()){SyntaxError(); return false;}
	        		break;
	        	case BKWgrabfile:
	        		if (!executeGRABFILE()){SyntaxError(); return false;}
	        		break;
	        	case BKWwakelock:
	        		if (!executeWAKELOCK()){SyntaxError(); return false;}
	        		break;
	        	case BKWwifilock:
	        		if (!executeWIFILOCK()){SyntaxError(); return false;}
	        		break;
	        	case BKWtone:
	        		if (!executeTONE()){SyntaxError(); return false;}
	        		break;
	        	case BKWlist:
	        		if (!executeLIST()){SyntaxError(); return false;}
	        		break;
	        	case BKWbundle:
	        		if (!executeBUNDLE()){SyntaxError(); return false;}
	        		break;
	        	case BKWstack:
	        		if (!executeSTACK()){SyntaxError(); return false;}
	        		break;
	        	case BKWdevice:
	        		if (!executeDEVICE()){SyntaxError(); return false;}
	        		break;
	        	case BKWhttp_post:
	        		if (!executeHTTP_POST()){SyntaxError(); return false;}
	        		break;
	        	case BKWsocket:
	        		if (!executeSOCKET()){SyntaxError(); return false;}
	        		break;
	        	case BKWdebug:
	        		if (!executeDEBUG()){SyntaxError(); return false;}
	        		break;
	        	case BKWconsole:
	        		if (!executeCONSOLE()){SyntaxError(); return false;}
	        		break;
	        	case BKWtts:
	        		if (!executeTTS()){SyntaxError(); return false;}
	        		break;
	        	case BKWftp:
	        		if (!executeFTP()){SyntaxError(); return false;}
	        		break;
	        	case BKWapp:
	        		if (!executeApp()) {SyntaxError(); return false;}
	        		break;
	        	case BKWf_n_continue:
	        		if (!executeF_N_CONTINUE()) {SyntaxError(); return false;}
	        		break;
	        	case BKWw_r_continue:
	        		if (!executeW_R_CONTINUE()) {SyntaxError(); return false;}
	        		break;
	        	case BKWd_u_continue:
	        		if (!executeD_U_CONTINUE()) {SyntaxError(); return false;}
	        		break;
	        	case BKWcall:
	        		if (!executeCALL()) {SyntaxError(); return false;}
	        		break;
	        	case BKWsu:
	        		if (!executeSU(true)) {SyntaxError(); return false;}
	        		break;
	        	case BKWsystem:
	        		if (!executeSU(false)) {SyntaxError(); return false;}
	        		break;
	        	case BKWtget:
	        		if (!executeTGET()) {SyntaxError(); return false;}
	        		break;
	        	case BKWf_n_break:
        			if (!executeF_N_BREAK()) {SyntaxError(); return false;}
        			break;
	        	case BKWw_r_break:
        			if (!executeW_R_BREAK()) {SyntaxError(); return false;}
        			break;
	        	case BKWd_u_break:
        			if (!executeD_U_BREAK()) {SyntaxError(); return false;}
        			break;
	        	case BKWtext_position_get:
	        		if (!executeTEXT_POSITION_GET()) {SyntaxError(); return false;}
	        		break;
	        	case BKWtext_position_set:
	        		if (!executeTEXT_POSITION_SET()) {SyntaxError(); return false;}
	        		break;
	        	case BKWbyte_position_set:
	        		if (!executeBYTE_POSITION_SET()) {SyntaxError(); return false;}
	        		break;
	        	case BKWbyte_position_get:
	        		if (!executeBYTE_POSITION_GET()) {SyntaxError(); return false;}
	        		break;
	        	case BKWbyte_read_buffer:
	        		if (!executeBYTE_READ_BUFFER()) {SyntaxError(); return false;}
	        		break;
	        	case BKWbyte_write_buffer:
	        		if (!executeBYTE_WRITE_BUFFER()) {SyntaxError(); return false;}
	        		break;
	        	case BKWsound_pool:
	        		if (!executeSP()) {SyntaxError(); return false;}
	        		break;
	        	case BKWmy_phone_number:
	        		if (!execute_my_phone_number()) {SyntaxError(); return false;}
	        		break;
	        	case BKWheadset:
	        		if (!executeHEADSET())  {SyntaxError(); return false;}
	        		break;
	        	case BKWsms_send:
	        		if (!executeSMS_SEND())  {SyntaxError(); return false;}
	        		break;
	        	case BKWphone_call:
	        		if (!executePHONE_CALL())  {SyntaxError(); return false;}
	        		break;
	        	case BKWemail_send:
	        		if (!executeEMAIL_SEND())  {SyntaxError(); return false;}
	        		break;
	        	case BKWhtml:
	        		if (!executehtml())   {SyntaxError(); return false;}
	        		break;
	        	case BKWrun:
	        		if (!executeRUN())   {SyntaxError(); return false;}
	        		break;
	        	case BKWempty_program:
	        		if (!executeEMPTY_PROGRAM())   {SyntaxError(); return false;}
	        		break;
	        	case BKWback_resume:
	        		if (!executeBACK_RESUME())   {SyntaxError(); return false;}
	        		break;
	        	case BKWnotify:
	        		if (!executeNOTIFY())   {SyntaxError(); return false;}
	        		break;
	        	case BKWswap:
	        		if (!executeSWAP())   {SyntaxError(); return false;}
	        		break;
	        	case BKWsms_rcv_init:
	        		if (!executeSMS_RCV_INIT())   {SyntaxError(); return false;}
	        		break;
	        	case BKWsms_rcv_next:
	        		if (!executeSMS_RCV_NEXT())   {SyntaxError(); return false;}
	        		break;
	        	case BKWstt_listen:
	        		if (!executeSTT_LISTEN())   {SyntaxError(); return false;}
	        		break;
	        	case BKWstt_results:
	        		if (!executeSTT_RESULTS())   {SyntaxError(); return false;}
	        		break;
	        	case BKWtimer:
	        		if (!executeTIMER())   {SyntaxError(); return false;}
	        		break;
	        	case BKWtimezone:
	        		if (!executeTIMEZONE())   {SyntaxError(); return false;}
	        		break;
	        	case BKWonkey_resume:
	        		if (!executeONKEY_RESUME())   {SyntaxError(); return false;}
	        		break;
	        	case BKWmenukey_resume:
	        		if (!executeONMENUKEY_RESUME())   {SyntaxError(); return false;}
	        		break;
	        	case BKWhome:
	        		if (!executeHOME())   {SyntaxError(); return false;}
	        		break;
	        	case BKWbackground_resume:
	        		if (!executeBACKGROUND_RESUME())   {SyntaxError(); return false;}
	        		break;
	        	case BKWphone_rcv_init:
	        		if (!executePHONE_RCV_INIT())   {SyntaxError(); return false;}
	        		break;
	        	case BKWphone_rcv_next:
	        		if (!executePHONE_RCV_NEXT())   {SyntaxError(); return false;}
	        		break;
	        	case BKWread_data:
	        		return true;		// Do NOT call executeREAD_DATA, that was done in PreScan
	        	case BKWread_next:
	        		if (!executeREAD_NEXT())   {SyntaxError(); return false;}
	        		break;
	        	case BKWread_from:
	        		if (!executeREAD_FROM())   {SyntaxError(); return false;}
	        		break;
	        	case BKWconsole_resume:
	        		if (!executeCONSOLE_RESUME())   {SyntaxError(); return false;}
	        		break;
	        	default:
	        		break;
	        } // end switch on KeyWordValue

		return true;     // Statement executed ok. Return to main looper.
	}
		
	private  void SyntaxError(){						// Called to output Syntax Error Message
/*		if (OnErrorLine != 0){
			return;
		}*/
		if (!SyntaxError){								// If a previous Syntax error message has
			RunTimeError("Syntax Error");					// not been displayed them display
			SyntaxError = true;							// Then set the flag so we don't do it again.
		}
		
		
		// If graphics is opened then the user will not be able to see error messages
		// Provide a Haptic notice
		
		if (GRopen){
			lv.performHapticFeedback(2, 1);
			try {Thread.sleep(300);}catch(InterruptedException e){}
			lv.performHapticFeedback(2, 1);
			try {Thread.sleep(300);}catch(InterruptedException e){}
			lv.performHapticFeedback(2, 1);
		}

	}

	private boolean RunTimeError(String... msgs){
		Show(msgs[0]);								// Display error message
		Show(ExecutingLineBuffer);					// Display offending line
		for (int i = 1; i < msgs.length; ++i) {		// Display any supplemental text
			Show(msgs[i]);
		}

		SyntaxError = true;
		errorMsg = msgs[0] + "\nLine: " + ExecutingLineBuffer;
		return false;						// Always return false as convenience for caller
	}

	private boolean RunTimeError(Exception e) {
		return RunTimeError("Error: " + e);
	}

   private boolean nextLine() {				// Move to beginning of next line
	   if (++ExecutingLineIndex < Basic.lines.size()) {	// if not at end of program
		   ExecutingLineBuffer = Basic.lines.get(ExecutingLineIndex);
		   LineIndex = 0;
		   return true;
	   }
	   --ExecutingLineIndex;				// No next line
	   return false;
   }

   private boolean isEOL(){
	   return (LineIndex >= ExecutingLineBuffer.length()) ||
			  (ExecutingLineBuffer.charAt(LineIndex) == '\n');
   }

   private boolean checkEOL(){
	   if (isEOL()) return true;
	   String ec = ExecutingLineBuffer.substring(LineIndex);
	   RunTimeError("Extraneous characters in line: " + ec);
	   return false;
   }

	private boolean isNext(char c) {		// Check the current character
		if ((LineIndex < ExecutingLineBuffer.length()) &&	// if it is as expected...
			(ExecutingLineBuffer.charAt(LineIndex) == c)) {
			++LineIndex;									// ... increment the character pointer
			return true;
		}
		return false;
	}

	private boolean getStringArg() {		// Get and validate a string
		return (evalStringExpression()			// Get the string expression
			&& !SEisLE							// Okay if not logical expression
			&& (StringConstant != null));		//      and not null
		// Leaves evaluation result in StringConstant
	}

	private boolean getArgAsNum() {			// Get and validate a numeric expression
		if (!evalNumericExpression()) {		// or string that evaluates to a number
			if (SyntaxError || !getStringArg()) { return false; }
			try { EvalNumericExpressionValue = Double.valueOf(StringConstant); }
			catch (NumberFormatException e) { return false; }
		}
		return true;							// return value in EvalNumericExpressionValue
	}

	private boolean GetKeyWord(){							// Get a Basic keyword if it is there
															// is the current line index at a keyword?
		for (int i = 0; i < BasicKeyWords.length; ++i) {	// loop through the keyword list
			if (ExecutingLineBuffer.startsWith(BasicKeyWords[i], LineIndex)) { // if there is a match
				KeyWordValue = i;							// set the keyword number
				LineIndex += BasicKeyWords[i].length();		// move the line index to end of keyword
				return true;								// and report back
			}
		}
		KeyWordValue = BKWnone;								// no keyword found
		return false;										// report fail
	}

	// ************************* start of getVar() and its derivatives ****************************

	private static final boolean TYPE_NUMERIC = true;	// true: type is numeric
	private static final boolean TYPE_STRING  = false;	// false: type is NOT numeric
	private static final boolean USER_FN_OK = true;		// flag used as parseVar() argument, when false
														// user-defined function names are not recognized as valid symbols

	private static final String EXPECT_ARRAY_VAR = "Array variable expected";
	private static final String EXPECT_NEW_ARRAY = "Array previously dimensioned";
	// private static final String EXPECT_UNDIM_ARRAY = "Array must not be DIMed";
	private static final String EXPECT_DIM_ARRAY = "Array must be DIMed before using";
	// private static final String EXPECT_DIM_ARRAY = "Array not DIMed";
	private static final String EXPECT_ARRAY_NO_INDEX = "Expected '[]'";
	private static final String EXPECT_NUM_ARRAY = "Array not numeric";
	private static final String EXPECT_STRING_ARRAY = "Not string array";
	private static final String EXPECT_NEW_FN_NAME = "Function previously defined at:";

	// getVar:
	// This function parses a function name out of the input stream, then searches for
	// it in the variable lists. If an existing variable is not found, it creates one.
	// It writes global flags, variables, and data structures for results and status.
	// There are other functions that duplicate getVar() except for small changes that
	// make certain cases more efficient by leaving out some of the work.
	//
	// In Paul's original design, this function handled all cases of scalar and array
	// variables, and user-defined function names for FN.DEF (but not function calls)
	// with special cases directed by global these global flags:
	//     doingDim, unDiming, SkipArrayValues, DoingDef
	// This implementation behaves as the original did when all flags were set false.
	// There are now dedicated functions for some of the special cases.
	// All other cases must be built up from the primitives found below.
	//
	private boolean getVar() {							// Get variable name if there is one.
		int LI = LineIndex;
		if (getVarValue(getVarAndType())) return true;	// If there is one, find it in the symbol table.
														// If not found in table, create new variable.
		LineIndex = LI;
		return false;
	}

	private boolean getNVar() {							// get var and assure that it is numeric
		int LI = LineIndex;
		if (getVarValue(getVarAndType(TYPE_NUMERIC))) return true;
		LineIndex = LI;
		return false;
	}

	private boolean getSVar() {							// get var and assure that it is not numeric
		int LI = LineIndex;
		if (getVarValue(getVarAndType(TYPE_STRING))) return true;
		LineIndex = LI;
		return false;
	}

	private String getArrayVarForWrite() {				// get the array var name as a new, undimensioned array
														// returns the name, does NOT create a variable
		int LI = LineIndex;
		String var = getVarAndType();					// either string or numeric type is ok
		if ((var == null) || !VarIsArray)	{ RunTimeError(EXPECT_ARRAY_VAR); }
		else if (!VarIsNew)					{ RunTimeError(EXPECT_NEW_ARRAY); }
		else if (!isNext(']'))				{ RunTimeError(EXPECT_ARRAY_NO_INDEX); }
		else return var;								// no error, return name, caller must create variable

		LineIndex = LI;									// error, return null
		return null;
	}

	private String getArrayVarForWrite(boolean type) {	// get the array var name as a new, undimensioned array
														// returns the name, does NOT create a variable
		int LI = LineIndex;
		String var = parseVar(!USER_FN_OK);
		if ((var == null) || !VarIsArray)	{ RunTimeError(EXPECT_ARRAY_VAR); }
		else if (type != VarIsNumeric)		{ RunTimeError(type ? EXPECT_NUM_ARRAY : EXPECT_STRING_ARRAY); }
		else if (searchVar(var))			{ RunTimeError(EXPECT_NEW_ARRAY); }
		else if (!isNext(']'))				{ RunTimeError(EXPECT_ARRAY_NO_INDEX); }
		else return var;								// no error, return name, caller must create variable

		LineIndex = LI;									// error, return null
		return null;
	}

	private String getArrayVarForRead() {				// get the array var as a previously-dimensioned array
														// returns the var name, null if error or no var
		int LI = LineIndex;
		String var = getVarAndType();					// type must match expected type
		if ((var == null) || !VarIsArray)	{ RunTimeError(EXPECT_ARRAY_VAR); }
		else if (VarIsNew)					{ RunTimeError(EXPECT_DIM_ARRAY); }
		else {
			return var;									// no error, array index is in theValueIndex
		}
		LineIndex = LI;
		return null;									// error, theVarIndex is not valid
	}

	private String getNewFNVar() {						// get var and assure that it is a new function name
														// returns the name, does NOT create a variable
		int LI = LineIndex;
		String var = parseVar(USER_FN_OK);				// Get function name if there is one.
		if ((var != null) && VarIsFunction) {			// If there is one...
			searchVar(var);								// ... look for it in the symbol table.
			if (VarIsNew) {
				return var;
			} else {
				RunTimeError(EXPECT_NEW_FN_NAME);
			}
		}
		LineIndex = LI;
		return null;
	}

	// ************************* top half of getVar() *************************

	private String getVarAndType() {
		String var = parseVar(!USER_FN_OK);				// Get variable name if there is one.
		if (var != null) {								// If there is one...
			searchVar(var);								// ... find it in the symbol table.
		}
		return var;										// note: LineIndex does not change if var is null
	}

	private String getVarAndType(boolean needNumeric) {	// specify TYPE_NUMERIC or TYPE_STRING
		String var = parseVar(!USER_FN_OK);				// get variable name if there is one
		if (var != null) {
			if (needNumeric == VarIsNumeric) {			// if type matches expected type
				searchVar(var);							// look up the variable name
				return var;								// note: parseVar changed LineIndex
			}											// else type mismatch, return no var
		}
		return null;									// note: LineIndex does not change
	}

	// Gets the variable name and type. Sets VarIsNumeric, VarIsArray, VarIsFunction.
	// Returns variable name. Name includes $ for strings, [ for arrays, ( for functions.
	// If arg is false, user-defined function names are not valid variable names.
	// Does not advance LineIndex unless it finds a valid variable name.
	private String parseVar(boolean isUserFnAllowed) {
		int LI = LineIndex;
		int max = ExecutingLineBuffer.length();

		// For the special cases where a var could be followed by keyword THEN, TO or STEP
		boolean stopOnPossibleKeyWord = !PossibleKeyWord.equals("");
																// Isolate the var characters
		String var = getWord(ExecutingLineBuffer, LI, stopOnPossibleKeyWord);
		if (var.length() == 0) { return null; }					// length is 0, no var
		LI += var.length();
		if (LI < max) {
			char c = ExecutingLineBuffer.charAt(LI);
			VarIsNumeric = (c != '$');							// Is this a string var?
			if (!VarIsNumeric && (++LI < max)) {
				var += c;
				c = ExecutingLineBuffer.charAt(LI);
			}
			VarIsArray = (c == '[');							// Is this an array?
			VarIsFunction = (c == '(');							// Is this a function?
			if (VarIsArray && (++LI < max)) {
				var += c;
			} else if (VarIsFunction && (++LI < max)) {
				if (!isUserFnAllowed) { return null; } 			// Do not write LineIndex
				var += c;
			}
		}
		if (LI >= max) { LineIndex = max; return null; }
		LineIndex = LI;
		return var;
	}

	private boolean searchVar(String var) {			// search for a variable by name
		int j = VarSearchStart;						// VarSearchStart is usually zero but will change when executing User Function
		for ( ; j < VarNames.size(); ++j) {			// look up this var in the variable table
			if (var.equals(VarNames.get(j))) {		// found it
				VarIsNew = false;
				VarNumber = j;
				theValueIndex = VarIndex.get(j);	// get the value index from the var table
				return true;
			}
		}
		VarIsNew = true;
		return false;								// not in list of variable names
	}

	// ************************* bottom half of getVar() **********************

	private boolean getVarValue(String var) {		// bottom half of getVar()
													// do NOT call before calling parseVar() and searchVar(String)
													// can automatically create salar but not array
		if (var == null) return false;				// no var to get
		if (VarIsArray) {
			if (VarIsNew) {							// new array: error
				return RunTimeError(EXPECT_DIM_ARRAY);
			} else {								// old array (has real VarIndex):
				return GetArrayValue();				// set theValueIndex based upon user's index values
			}
		} else if (VarIsNew) {
			createNewScalar(var);					// create new scalar with real VarIndex, theVarIndex is valid
		}
		return true;
	}

	private int createNewVar(String var) {			// make a new var table entry
		VarNumber = VarNames.size();
		VarNames.add(var);
		VarIndex.add(0);							// keep VarIndex in sync
		return VarNumber;
	}

	private void createNewScalar(String var) {		// make a new var table entry and put a scalar in it
		int kk = 0;
		if (!VarIsNumeric) {						// if var is string
			kk = StringVarValues.size();			// then the value index is the index
			StringVarValues.add("");				// to the next unused entry in string values list
		} else {									// else var is numeric
			kk = NumericVarValues.size();			// then the value index is the index
			NumericVarValues.add(0.0);				// to the next unused entry in numeric values list
		}
		VarNumber = VarNames.size();				// make a new var table entry
		VarNames.add(var);
		VarIndex.add(kk);							// The var index is the next avail Numeric Value
		theValueIndex = kk;
	}

	// ************************************* end of getVar() **************************************

	private  boolean getNumber(){						// Get a number if there is one
														// Attempt to parse out a number
		char c = 0;
		int max = ExecutingLineBuffer.length();
		int i = LineIndex;
		while (i < max) {								// Must start with one or more digits
			c = ExecutingLineBuffer.charAt(i);
			if (c > '9' || c < '0') { break; }			// If not a digit, done with whole part
			++i;
		}
		if (i == LineIndex) { return false; }			// No digits, not a number

		if (c == '.') {									// May have a decimal point
			while (++i < max) {							// Followed by more digits
				c = ExecutingLineBuffer.charAt(i);
				if (c > '9' || c < '0') { break; }		// If not a digit, done with fractional part
			}
		}
		if (c == 'e' || c == 'E') {						// Is there an exponent
			if (++i < max) {
				c = ExecutingLineBuffer.charAt(i);
				if (c == '+' || c == '-') { ++i; }		// Is there a sign on the exponent
			}
			while (i < max) {							// Get the exponent
				c = ExecutingLineBuffer.charAt(i);
				if (c > '9' || c < '0') { break; }		// If not a digit, done with exponent
				++i;
			}
		}
		String num = ExecutingLineBuffer.substring(LineIndex,i);			// isolate the numeric characters
		LineIndex = i;
		double d = 0;
		try { d = Double.parseDouble(num);}									// have java parse it into a double
		catch (Exception e) {
			return RunTimeError(e);
		}
        GetNumberValue = d;													// Report the value 
		return true;														// Say we found a number
	}
	
	private  boolean GetStringConstant(){				// Get a string constant if there is one
																		// parse out string constant
		int max = ExecutingLineBuffer.length();
		if (LineIndex >= max || LineIndex < 0) return false;
		
		int i = LineIndex;
		StringConstant = "";
		char c = ExecutingLineBuffer.charAt(i);
		if (c!= '"'){return false;}										// first char not "", not String Constant
		while (true) {													// Get the rest of the String
			++i;														// copy character until " or EOL
			if (i >= max) return false;
			c = ExecutingLineBuffer.charAt(i);
			if (c == '"') break;										// if " we're done

			if (c == '\r') {			// AddProgramLine hides embedded newline as carriage return
				c = '\n';
			} else if (c == '\\') {		// AddProgramLine allows only quote or backslash after backslash
				c = ExecutingLineBuffer.charAt(++i);
			}
			StringConstant += c;										// add to String Constant
		}
		
        if (i< max-1){ ++i;}							// do not let index be >= line length
        LineIndex = i;
		return true;															// Say we have a string constant
	}
		
	private  boolean MathFunction(){					// get a Math Function if there is one
		int i = ExecutingLineBuffer.indexOf('(', LineIndex);
		if (i < 0) { return false; }								// no '(', so no function
		String token = ExecutingLineBuffer.substring(LineIndex, i + 1);	// tokoen could be a function name
		for (i = 0; i < MathFunctions.length; ++i) {
			if (token.equals(MathFunctions[i])) {					// Is the token a math function name?
				MFNumber = i;										// If it is, set the Enumerated name
				LineIndex += token.length();						// set line index to end of MF
				return (LineIndex < ExecutingLineBuffer.length());	// If no error, we have a math function
			}
		}
		return false;												// Not a math function
	}
	
	private  boolean executeLET(){						// Execute a real or pseudo LET
															// Execute LET (an assignment statement)
		if (!getVar()){										// Must start with a variable
			return false;									// to assign a value to
		}
		
		if (isNext(':')) {									// If this is a label,			
			return checkEOL();								// then done
		}
		
		if (!isNext('=')) {									// Var must be followed by =
			return false;
		}
		
//		if (theValueIndex == null) return false;            // Why? Because it is sometimes null
		
		int AssignToVarNumber = theValueIndex;				// Save the assign to Var Number
		
		if (VarIsNumeric) {									// if var is number then 
			if (!evalNumericExpression()) { return false; }	// evaluate following numeric expression
			NumericVarValues.set(AssignToVarNumber, EvalNumericExpressionValue);  // and assign results to the var
		} else {											// Assignment is string expression
			if (!getStringArg()) { return false; }			// Evaluate the string expression
			StringVarValues.set(AssignToVarNumber, StringConstant); // Assign result to the string var
		}
		return checkEOL();
	}	
	
	private  boolean evalNumericExpression(){			// Evaluate a numeric expression

		if (LineIndex >= ExecutingLineBuffer.length() ) return false;
															// Evaluate a Numeric Expression
		char c = ExecutingLineBuffer.charAt(LineIndex);
		if (c == '\n' || c == ')') { return false; }		// If eol or starts with ')', there is not an expression

		Stack<Double> ValueStack = new Stack<Double>();     // Each call to eval gets its own stack
		Stack<Integer>OpStack = new Stack<Integer>();		// thus we can recursively call eval
		int SaveIndex = LineIndex;

		OpStack.push(SOE);									// Push Start of Expression onto stack
		if (!ENE(OpStack, ValueStack)) {					// Now do the recursive evaluation
			LineIndex = SaveIndex;							// if it fails, back up
			return false;									// and die
		}

		if (ValueStack.empty()) return false;
		EvalNumericExpressionValue = ValueStack.pop();		// Recursive eval succeeded. Pop stack for results
		return true;
	}
		
	private boolean ENE(Stack<Integer> theOpStack, Stack<Double> theValueStack){ // Part of evaluating a number expression

																// The recursive part of Eval Expression
		Bundle ufb = ufBundle;
			
		char c = ExecutingLineBuffer.charAt(LineIndex);         // First character
 
		if (c == '+'){											// Check for unary operators
			theOpStack.push(UPLUS);
			++LineIndex;
		}
		else if(c == '-'){
			theOpStack.push(UMINUS);
			++LineIndex;
		}
		else if (c == '!'){
			theOpStack.push(NOT);
			++LineIndex;
		}
		
		if (getNumber()){								// Not a var, must be a number
			theValueStack.push(GetNumberValue);			// if it is, push the number
			}
		
		else if (getNVar()){							// Try numeric variable									
			theValueStack.push(NumericVarValues.get(theValueIndex));	// Push value of Var
		}

		else if (MathFunction()){						// Try Match Function
			if (!doMathFunction(theOpStack, theValueStack )) return false;
		}
		
		else if (evalStringExpression()){		           // Try String Logical Expression
			if (!SEisLE) return false;                     // If was not a logical string expression, fail
			theValueStack.push(EvalNumericExpressionValue);			
		}

		else if (isUserFunction(TYPE_NUMERIC)) {			// Try User Function
			if (!doUserFunction()) return false;
			ufBundle = ufb;
			theValueStack.push(EvalNumericExpressionValue);
		}
		else if (isNext('(')) {								// Handle possible (
			String holdPKW = PossibleKeyWord;
			PossibleKeyWord = "";
			if (!evalNumericExpression()){return false;} 	// if ( then eval expression inside the parens
			theValueStack.push(EvalNumericExpressionValue);
			PossibleKeyWord = holdPKW;
			if (!isNext(')')) {return false;}
		}
		else {
			return false;									// nothing left, fail
		}

		if (LineIndex >= ExecutingLineBuffer.length() ) return false;
		c = ExecutingLineBuffer.charAt(LineIndex);
		
		int k = LineIndex;
		
		if (!PossibleKeyWord.equals("")){
			if (ExecutingLineBuffer.startsWith(PossibleKeyWord, LineIndex)){
				return handleOp(EOL,  theOpStack, theValueStack);
			}
		}

		if (",:;]".indexOf(c) >= 0) {						// treat any of these characters as an eol
			return handleOp(EOL, theOpStack, theValueStack);
		}

		k = LineIndex;
		if (!getOp()){return false;}						// If operator does not follow, then fail
		
		switch  (OperatorValue){							// Handle special case operators
															// (This is probably reduntant given the above)
		case EOL:
			if (!handleOp(EOL,  theOpStack, theValueStack)){return false;}
			--LineIndex;
			return true;
		case RPRN:
			if (!handleOp(RPRN,  theOpStack, theValueStack)){return false;}
			if (theOpStack.isEmpty()) { return true; }		// ')' was removed with matching '('
			if ((theOpStack.pop() == RPRN) && !theOpStack.isEmpty()) {
				if (theOpStack.pop() == SOE) {
					--LineIndex;							// LineIndex points at ')' 
					return true;							// Let caller try to match ')'
				}
			}
			return false;									// op stack got corrupted?
		case NOT:											// can't use unary operator after operand
		case LPRN:											// can't start new expression after operand
			return false;
		default:
			if (!handleOp(OperatorValue,  theOpStack, theValueStack)){return false;} // Handles non special case ops
		}

		return ENE(theOpStack, theValueStack);				// Recursively call ENE for rest of expression
	}

	private boolean getOp(){								// Get an expression operator if there is one

		int lastOp = OperatorString.length;					// Look for operator
		for (int i = 0; i < lastOp; ++i) {
			String op = OperatorString[i];
			if (ExecutingLineBuffer.startsWith(op, LineIndex)) {
				OperatorValue = i;
				LineIndex += op.length();
				return true;
			}
		}
		if (isNext('~')) {									// Look for the array.load continue line character
			OperatorValue = EOL;							// Change it to EOL
			return true;
		}
		return false;
	}

	private  boolean handleOp(int op, Stack<Integer> theOpStack, Stack<Double> theValueStack ){	// handle an expression operator

											// Execute operator in turn by their precedence

		double d1 = 0;
		double d2 = 0;
		int ExecOp = 0;

											// If the operator stack is empty, push an SOE (should never happen)
		if (theOpStack.empty()) {
//			theOpStack.push(SOE);
			return false;
		}
											// If the current operator's Goes Onto Stack Precedence
											// is less than the top of stack' Come Off precedence
											// then pop the top of stack operator and execute it
											// keep doing this until the Goes Onto Precedence
											// is less then the TOS Come Off Precedence and then
											// push the current operator onto the operator stack
		
		while (ComesOffPrecedence[theOpStack.peek()] >= GoesOnPrecedence[op]){

				if (theValueStack.empty()) return false;	// Avoid a crash
				ExecOp = theOpStack.pop();

											// Execute the popped operator
											// In general values are popped from the stack and then
											// operated on by the operator
											// the result is then pushed onto the value stack
				switch (ExecOp){
				case UMINUS:
					d1 = theValueStack.pop();
					d1 = -d1;
					theValueStack.push(d1);
					break;
				case UPLUS:
					break;
					
				case PLUS:
					d1 = theValueStack.pop();
					d2 = theValueStack.pop();
					d1 = d2 + d1;
					theValueStack.push(d1);
					break;
					
				case MINUS:
					d1 = theValueStack.pop();
					d2 = theValueStack.pop();
					d1 = d2 - d1;
					theValueStack.push(d1);
					break;

				case MUL:
					d1 = theValueStack.pop();
					d2 = theValueStack.pop();
					d1 = d2 * d1;
					theValueStack.push(d1);
					break;

				case DIV:
					d1 = theValueStack.pop();
					d2 = theValueStack.pop();
					// handle divide by zero
					if (d1 == 0) {
						return RunTimeError("DIVIDE BY ZERO AT:");
					}
					d1 = d2 / d1;
					theValueStack.push(d1);
					break;

				case EXP:
					d1 = theValueStack.pop();
					d2 = theValueStack.pop();
					d1 = Math.pow(d2,d1);
					theValueStack.push(d1);
					break;
					
				case LE:
					d1 = theValueStack.pop();
					d2 = theValueStack.pop();
					d1 = (d2 <= d1) ? 1.0 : 0.0;
					theValueStack.push(d1);
					break;
					
				case NE:
					d1 = theValueStack.pop();
					d2 = theValueStack.pop();
					d1 = (d2 != d1) ? 1.0 : 0.0;
					theValueStack.push(d1);
					break;
					
				case GE:
					d1 = theValueStack.pop();
					d2 = theValueStack.pop();
					d1 = (d2 >= d1) ? 1.0 : 0.0;
					theValueStack.push(d1);
					break;
					
				case GT:
					d1 = theValueStack.pop();
					d2 = theValueStack.pop();
					d1 = (d2 > d1) ? 1.0 : 0.0;
					theValueStack.push(d1);
					break;
					
				case LT:
					d1 = theValueStack.pop();
					d2 = theValueStack.pop();
					d1 = (d2 < d1) ? 1.0 : 0.0;
					theValueStack.push(d1);
					break;
					
				case LEQ:						// Logical Equals 
					d1 = theValueStack.pop();
					d2 = theValueStack.pop();
					d1 = (d2 == d1) ? 1.0 : 0.0;
					theValueStack.push(d1);
					break;

				case OR:
					d1 = theValueStack.pop();
					d2 = theValueStack.pop();
					d1 = ((d1 != 0) || (d2 != 0)) ? 1.0 : 0.0;
					theValueStack.push(d1);
					break;

				case AND:
					d1 = theValueStack.pop();
					d2 = theValueStack.pop();
					d1 = ((d1 != 0) && (d2 != 0)) ? 1.0 : 0.0;
					theValueStack.push(d1);
					break;

				case NOT:
					d1 = theValueStack.pop();
					d1 = (d1 == 0) ? 1.0 : 0.0;
					theValueStack.push(d1);
					break;
				case LPRN:
					if (op != RPRN) { return false; }
					break;
				case FLPRN:
					break;
				case RPRN:
					break;
				case SOE:
					return true;
				case EOL:
					d1=d2;
					break;
				default:
				}
//			if (op == RPRN) break;


		}  // End of while pop stack operations
		
		theOpStack.push(op);          // Push the current operator
		return true;
	}

	private boolean doMathFunction(Stack<Integer> theOpStack, Stack<Double> theValueStack){
		double d1 = 0.0;
		double d2 = 0.0;

		switch (MFNumber) {

			case MFsin:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.sin(d1));
				break;

			case MFcos:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.cos(d1));
				break;

			case MFtan:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.tan(d1));
				break;

			case MFsqr:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.sqrt(d1));
				break;

			case MFabs:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.abs(d1));
				break;

			case MFsgn:											// 2014-03-16 gt
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.signum(d1));
				break;

			case MFceil:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.ceil(d1));
				break;

			case MFfloor:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.floor(d1));
				break;

			case MFint:											// 2014-03-16 gt
				if (!evalNumericExpression()) return false;
				d1 = (double)(EvalNumericExpressionValue.intValue());
				theValueStack.push(Double.valueOf(d1));
				break;

			case MFfrac:										// 2014-03-16 gt
				if (!evalNumericExpression()) return false;
				String s1 = EvalNumericExpressionValue.toString();
				String s2 = (EvalNumericExpressionValue < 0) ? "-" : "";
				int i1 = s1.indexOf('.');
				d1 = (i1 < 0) ? 0 : Double.parseDouble(s2 + s1.substring(i1));
				theValueStack.push(d1);
				break;

			case MFmin:											// 2013-07-29 gt
				if (!evalNumericExpression()){return false;}
				d1 = EvalNumericExpressionValue;
				if (!isNext(',')) { return false; }
				if (!evalNumericExpression()){return false;}
				d2 = EvalNumericExpressionValue;
				theValueStack.push(Math.min(d1, d2));
				break;

			case MFmax:											// 2013-07-29 gt
				if (!evalNumericExpression()){return false;}
				d1 = EvalNumericExpressionValue;
				if (!isNext(',')) { return false; }
				if (!evalNumericExpression()){return false;}
				d2 = EvalNumericExpressionValue;
				theValueStack.push(Math.max(d1, d2));
				break;

			case MFlog:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.log(d1));
				break;

			case MFexp:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.exp(d1));
				break;
			
			case MFtodegrees:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.toDegrees(d1));
				break;

			case MFtoradians:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.toRadians(d1));
				break;

			case MFpi:											// 2013-07-29 gt
				theValueStack.push(Math.PI);
				break;

			case MFatan:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.atan(d1));
				break;

			case MFacos:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				if (d1<-1 || d1> 1){
					RunTimeError("ACOS parameter out of range");
					return false;
				}
				theValueStack.push(Math.acos(d1));
				break;

			case MFasin:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				if (d1<-1 || d1> 1){
					RunTimeError("ASIN parameter out of range");
					return false;
				}
				theValueStack.push(Math.asin(d1));
				break;

			case MFround:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;					// look for optional place count arg
				if (!isNext(',')) {									// no optional parameters, legacy behavior
					d1 = (double)Math.round(d1);
				} else {
					int places = 0;									// default decimal place count
					int roundingMode = BigDecimal.ROUND_HALF_DOWN;	// default rounding mode
					boolean isComma = isNext(',');
					if (!isComma) {
						if (!evalNumericExpression()) return false;	// get decimal place count
						places = EvalNumericExpressionValue.intValue();
						if (places < 0) { return RunTimeError("Decimal place count (" + places + ") must be >= 0"); }
						isComma = isNext(',');
					}
					if (isComma) {									// look for optional rounding mode
						if (!getStringArg()) return false;
						String roundingArg = StringConstant.toLowerCase(Locale.US);
						Integer mode = mRoundingModeTable.get(roundingArg);
						if (mode == null) { return RunTimeError("Invalid rounding mode: " + roundingArg); }
						roundingMode = mode.intValue();
					}
					d1 = new BigDecimal(d1).setScale(places, roundingMode).doubleValue();
				}
				theValueStack.push(d1);
				break;

			case MFlen:								// LEN(s$
				if (!getStringArg()) { return false; }			// Get and check the string expression
				 d1= (double) StringConstant.length();			// then get it's length
				 theValueStack.push(d1);						// and push onto value stack
				 break;
			
			case MFrandomize:
				if (!evalNumericExpression()) return false;
			    d1 = EvalNumericExpressionValue;
				if ( d1 == 0 )randomizer = new Random();
				else randomizer = new Random((long) d1);
				theValueStack.push(0.0);
				break;
			
			
			case MFrnd:
				if (randomizer == null) randomizer = new Random();
				theValueStack.push( randomizer.nextDouble());
				break;
			
			case MFbackground:
				if (background) theValueStack.push( 1.0);
				else theValueStack.push( 0.0 );
				break;


			case MFval:								// VAL(s$
				if (!getStringArg()) { return false; }			// Get and check the string expression
				StringConstant = StringConstant.trim();
				if (StringConstant.length()== 0 ) {
					RunTimeError("VAL of empty string is not valid");
					return false;
				}
			
				try { d1 = Double.parseDouble(StringConstant);}			// have java parse it into a double
				catch (Exception e) {
					return RunTimeError(e);
				}
				theValueStack.push(d1);							// Push number onto value stack
				break;
			
			case MFascii:
				if (!getStringArg()) { return false; }			// Get and check the string expression
				if (StringConstant.equals("")) d1 =256;
				else d1 = (double)(StringConstant.charAt(0) & 0x00FF);
				theValueStack.push(d1);							// Push number onto value stack
				break;

			case MFucode:
				if (!getStringArg()) { return false; }			// Get and check the string expression
				if (StringConstant.equals("")) d1 = 0x10000;
				else d1 = (double)(StringConstant.charAt(0));
				theValueStack.push(d1);							// Push number onto value stack
				break;
			
			case MFmod:									// MOD( d1,d2
				if (!evalNumericExpression()){return false;}				// Get d1
				d1 = EvalNumericExpressionValue;
				if (!isNext(',')) { return false; }
				if (!evalNumericExpression()){return false;}				// Get d2
				d2 = EvalNumericExpressionValue;
				
				if (d2==0){
					RunTimeError("DIVIDE BY ZERO AT:");
					return false;
				}
				theValueStack.push(d1 % d2);
				break;
			
			case MFbor:
				if (!evalNumericExpression()){return false;}				// Get d1
				d1 = EvalNumericExpressionValue;
				if (!isNext(',')) { return false; }
				if (!evalNumericExpression()){return false;}				// Get d2
				d2 = EvalNumericExpressionValue;
				theValueStack.push((double)((long)d1 | (long)d2));
				break;
			
			
			case MFband:
				if (!evalNumericExpression()){return false;}				// Get d1
				d1 = EvalNumericExpressionValue;
				if (!isNext(',')) { return false; }
				if (!evalNumericExpression()){return false;}				// Get d2
				d2 = EvalNumericExpressionValue;
				theValueStack.push((double)((long)d1 & (long)d2));
				break;
			
			
			case MFbxor:
				if (!evalNumericExpression()){return false;}				// Get d1
				d1 = EvalNumericExpressionValue;
				if (!isNext(',')) { return false; }
				if (!evalNumericExpression()){return false;}				// Get d2
				d2 = EvalNumericExpressionValue;
				theValueStack.push((double)((long)d1 ^ (long)d2));
				break;
			

				
			case MFis_in:
				if (!getStringArg()) { return false; }			// Get and check the string expression
	        	String Search_for = StringConstant;               
				if (!isNext(',')) { return false; }
				if (!getStringArg()) { return false; }			// Get and check the string expression
	        	String Search_in = StringConstant;
	        	
	        	int start = 1;
				if (isNext(',')) {
					if (!evalNumericExpression()) return false;
					start = EvalNumericExpressionValue.intValue();
					if (start < 1 ){
						RunTimeError("Start value must be >= 1");
						return false;
					}
	        	}
	        	start = start - 1;									// Make start index zero-based

	        	if (start < Search_in.length()) {					// If starting inside the string
					int k = Search_in.indexOf(Search_for, start);	// Do the search
					d1 = (double) k+1 ;								// Make results one based
				} else {
					d1 = 0.0;										// else "not found"
				}
	        	theValueStack.push(d1);                     		// Push result onto stack
				break;
			
			case MFstarts_with:
				if (!getStringArg()) { return false; }			// Get and check the string expression
	        	Search_for = StringConstant;               
				if (!isNext(',')) { return false; }
				if (!getStringArg()) { return false; }			// Get and check the string expression
	        	Search_in = StringConstant;
	        	
	        	start = 1;
				if (isNext(',')) {
					if (!evalNumericExpression()) return false;
					d1 = EvalNumericExpressionValue;
					start = (int) d1;
					if (start < 1 ){
						RunTimeError("Start value must be >= 1");
						return false;
					}
	        	}
	        	if (start > Search_in.length()) start = Search_in.length();
	        	start = start - 1;

	        	d1 = 0;
	        	if (Search_in.startsWith(Search_for, start)){
	        		d1 = Search_for.length();
	        	}

	        	theValueStack.push(d1);                     		// Push result onto stack
				break;

			case MFends_with:
				if (!getStringArg()) { return false; }			// Get and check the string expression
	        	Search_for = StringConstant;               
				if (!isNext(',')) { return false; }
				if (!getStringArg()) { return false; }			// Get and check the string expression
	        	Search_in = StringConstant;
	        	
	        	start = 1;
				if (isNext(',')) {
					if (!evalNumericExpression()) return false;
					d1 = EvalNumericExpressionValue;
					start = (int) d1;
					if (start < 1 ){
						RunTimeError("Start value must be >= 1");
						return false;
					}
	        	}
	        	if (start > Search_in.length()) start = Search_in.length();
	        	start = start - 1;

	        	d1 = 0;
	        	if (Search_in.endsWith(Search_for)){
	        		d1 = Search_in.length()-Search_for.length()+1;
	        	}

	        	theValueStack.push(d1);                     		// Push result onto stack
				break;
			
			case MFclock:
				theValueStack.push((double) SystemClock.elapsedRealtime());
				break;
			
			case MFtime:
				if (ExecutingLineBuffer.charAt(LineIndex)== ')') {	// If no args, use current time
					theValueStack.push((double) System.currentTimeMillis());
				} else {											// Otherwise, get user-supplied time
					Time time = theTimeZone.equals("") ? new Time() : new Time(theTimeZone);
					if (!parseTimeArgs(time)) { return false; }
					theValueStack.push((double) time.toMillis(true));
				}
				break;
			
			case MFcollision:
				if (!evalNumericExpression()){return false;}	// Get the first object number
	        	int Object1 = EvalNumericExpressionValue.intValue();
				if (!isNext(',')) { return false; }
				if (!evalNumericExpression()){return false;}	// Get the second object number
	        	int Object2 = EvalNumericExpressionValue.intValue();

				double ff = gr_collide(Object1, Object2);
				if (ff == -1) return false;							// -1 is run time error
				theValueStack.push(ff);								// else f = 0 (false) or 1 (true)
				break;
			
			
			case MFhex:
				if (!getStringArg()) { return false; }			// Get and check the string expression
	        	long  value = 0;
	        	try {
	        		value = Integer.parseInt(StringConstant, 16);
	        	} catch (Exception e) {
	        		RunTimeError("Error" + e);
	        		return false;
	        	}
				value = value & 0xffffffff; 

	        	theValueStack.push((double) value);
				break;
			
			
			case MFoct:
				if (!getStringArg()) { return false; }			// Get and check the string expression
	        	value = 0;
	        	try {
	        		value = Integer.parseInt(StringConstant, 8);
	        	} catch (Exception e) {
	        		RunTimeError("Error" + e);
	        		return false;
	        	}
				value = value & 0xffffffff; 

	        	theValueStack.push((double) value);
				break;
			
			case MFbin:
				if (!getStringArg()) { return false; }			// Get and check the string expression
	        	value = 0;
	        	try {
	        		value = Integer.parseInt(StringConstant, 2);
	        	} catch (Exception e) {
	        		RunTimeError("Error" + e);
	        		return false;
	        	}
				value = value & 0xffffffff; 
	        	theValueStack.push((double) value);
				break;
			
			
			case MFshift:
				if (!evalNumericExpression()){return false;}	// Get the value to shift
	        	int xvalue = EvalNumericExpressionValue.intValue();
				if (!isNext(',')) { return false; }
				if (!evalNumericExpression()){return false;}	// Get the shift amount and direction
	        	int bits = EvalNumericExpressionValue.intValue();

				int result = 0;
				if (bits < 0){
					result = xvalue << -1*bits;
				}else{
					result = xvalue >> bits;
				}
				
				theValueStack.push((double) result);
				break;
				
			case MFatan2:
				if (!evalNumericExpression()) return false;		// Get the x-coordinate
				d1 = EvalNumericExpressionValue;
				if (!isNext(',')) { return false; }
				if (!evalNumericExpression()){return false;}	// Get the y-coordinate
	        	d2 = EvalNumericExpressionValue;
				theValueStack.push(Math.atan2(d1, d2));
				break;

			case MFcbrt:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.cbrt(d1));
				break;

			case MFcosh:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.cosh(d1));
				break;

			case MFhypot:
				if (!evalNumericExpression()) return false;		// Get x
				d1 = EvalNumericExpressionValue;
				if (!isNext(',')) { return false; }
				if (!evalNumericExpression()){return false;}	// Get y
	        	d2 = EvalNumericExpressionValue;
				theValueStack.push(Math.hypot(d1, d2));
				break;

			case MFsinh:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.sinh(d1));
				break;

			case MFpow:
				if (!evalNumericExpression()) return false;		// Get the base
				d1 = EvalNumericExpressionValue;
				if (!isNext(',')) { return false; }
				if (!evalNumericExpression()){return false;}	// Get the exponent
	        	d2 = EvalNumericExpressionValue;
				theValueStack.push(Math.pow(d1, d2));
				break;

			case MFlog10:
				if (!evalNumericExpression()) return false;
				d1 = EvalNumericExpressionValue;
				theValueStack.push(Math.log10(d1));
				break;

			default:
				break;
		}
		// Every function must have a closing right parenthesis.
		return (isNext(')'));
	}

	private  boolean getStringFunction(){				// Get a string function if there is one
													// are the next characters a String Function?
													// start by chopping off the part of the line
													// that we are done with
		
		int i = 0;
		for (i = 0; i<StringFunctions.length; ++i){				// scan the String Function List
			 if (ExecutingLineBuffer.startsWith(StringFunctions[i],LineIndex)){					// if in list
				 SFNumber = i;											// the function number is the table index
				 LineIndex = LineIndex + StringFunctions[i].length();	// move the line index over the function
				 return true;											// report found
			 	}
			}
		return false;													// report fail
	}

	private boolean evalStringExpression(){						// Evaluate a string expression

		int max = ExecutingLineBuffer.length();
		if (LineIndex >= max) { return false; }

		char c = ExecutingLineBuffer.charAt(LineIndex);
		if (c == '\n' || c == ')') { return false; }			// If eol or starts with ')', there is not an expression

		SEisLE = false;											// Assume not Logical Expression

		String Temp1 = "";
		do {
			if (!ESE()) { return false; }						// Get the next element (constant, var, function, etc)
			Temp1 += StringConstant;							// save the resulting string 
		} while (isNext('+'));									// Another piece to concatenate?

		StringConstant = Temp1;
		EvalNumericExpressionValue = 0.0;						// Set Logical Compare Result to false
		if (LineIndex >= max) { return false; }
		c = ExecutingLineBuffer.charAt(LineIndex);
		if ((c == '\n') ||		// end of line
			(c == ')')  ||		// end of parenthesized expression
			(c == ',')  ||		// parameter separator
			(c == ';')  ||		// PRINT separator
			(c == ':')			// SQL.UPDATE separator
			) { return true; }									// string expression done

																// logical comparison operator required
		int SaveLineIndex = LineIndex;
		boolean isOp = getOp();
		int operator = OperatorValue;
		isOp &=	operator == LE  ||
				operator == NE  ||
				operator == GE  ||
				operator == GT  ||
				operator == LT  ||
				operator == LEQ ;
		if (!isOp) {											// not a logical comparison op
			LineIndex = SaveLineIndex;
			return true;										// string expression done
		}

		if (!ESE()) { return false; }							// get the string to compare

		SEisLE = true;											// signal logical string expression
		String Temp2 = StringConstant;							// do any concat on the right side
		while (isNext('+')) {
			SaveLineIndex = LineIndex - 1;						// index of the '+'
			if (ESE()) {
				Temp2 += StringConstant;						// build up the right side string
			} else {											// what follows is not a string expression
				LineIndex = SaveLineIndex;						// assume the + operation is numeric
				break;
			}
		}

		if (Temp1 ==  null || Temp2 == null) { return false; }
		int cv = Temp1.compareTo(Temp2);						// Do the compare
		/* if Temp1 < Temp2, cv < 0
		 * if Temp1 = Temp2, cv = 0
		 * if Temp1 > Temp2, cv > 0
		 */

		EvalNumericExpressionValue = 0.0;						// assume false

		switch (operator) {

		case LE:
			if (cv <= 0) EvalNumericExpressionValue = 1.0;
			break;
		case NE:
			if (cv != 0) EvalNumericExpressionValue = 1.0;
			break;
		case GE:
			if (cv >= 0) EvalNumericExpressionValue = 1.0;
			break;
		case GT:
			if (cv > 0) EvalNumericExpressionValue = 1.0;
			break;
		case LEQ:
			if (cv == 0) EvalNumericExpressionValue = 1.0;
			break;
		case LT:
			if (cv < 0) EvalNumericExpressionValue = 1.0;
			break;
		default:
			return false;										// Can't happen
		}
		return true;
	}

	private boolean ESE(){										// Get a String expression element
 
		if (GetStringConstant()) { return true; }				// Try String Constant

		int LI = LineIndex;

		if (isNext('(')) {										// Try parenthesized string expression
			if (getStringArg() && isNext(')')) { return true; }	// logical expresson not allowed here
			LineIndex = LI;
			return false;
		}

		// Try string variable
		String var = getVarAndType();							// top half of getVar()
		if (var != null) {
			if (VarIsNumeric) {
				LineIndex = LI;
				return false;
			}
			if (getVarValue(var)) {								// bottom half of getVar()
				StringConstant = StringVarValues.get(theValueIndex);
				return true;
			}
		}
		LineIndex = LI;

		boolean flag = false;
		Bundle ufb = ufBundle;
		if (isUserFunction(TYPE_STRING)) {
			flag = doUserFunction();
			ufBundle = ufb;
		} else {
			LineIndex = LI;										// Try String Functions
			flag = (getStringFunction() && doStringFunction());
		}
		if (!flag) { LineIndex = LI; }
		return flag;
	}

	private boolean doStringFunction() {
		double d = 0;
		int e = 0;
		int e1 = 0;
		int length;
		
		String SSC;
																// Have a string function, do it
		switch (SFNumber){
			case  SFleft:																		// LEFT$
				if (!getStringArg()) { return false; }
				if (!isNext(',')) { return false; }
				SSC = StringConstant;
				if (!evalNumericExpression()) { return false; }
				if (!isNext(')')) { return false; }				// Function must end with ')'

				if (SSC.length() > 0) {
					e = EvalNumericExpressionValue.intValue();
					if (e <= 0) { SSC = ""; }
					else if (e < SSC.length()) { SSC = SSC.substring(0, e); }
				}
				StringConstant = SSC;
				return true;

			case SFmid:																			// MID$
				if (!getStringArg()) { return false; }			// Get the string
				SSC = StringConstant;
				length = SSC.length();

				if (!isNext(',')) { return false; }
				if (!evalNumericExpression()) { return false; }	// Get the start index
				e = EvalNumericExpressionValue.intValue();

				if (isNext(',')) {								// If there is a count, get it
					if (!evalNumericExpression()) { return false; }
					e1 = EvalNumericExpressionValue.intValue();
					if (e1 < 0) { return RunTimeError("Count < 0"); }
				} else {
					e1 = length;								// Default count is whole string
				}
				if (!isNext(')')) { return false; }				// Function must end with ')'

				if (length > 0) {
					if (e > length) { SSC = ""; }
					else {
						if (--e < 0) { e = 0; }					// Change 1-based index to 0-based
						e1 += e;								// Change count to end index
						if (e1 > length) { e1 = length; }
						SSC = SSC.substring(e, e1);
					}
				}
				StringConstant = SSC;
				return true;

			case SFright:																		// RIGHT$
				if (!getStringArg()) { return false; }
				if (!isNext(',')) { return false; }
				SSC = StringConstant;
				if (!evalNumericExpression()) { return false; }
				if (!isNext(')')) { return false; }				// Function must end with ')'

				length = SSC.length();
				if (length > 0) {
					e = EvalNumericExpressionValue.intValue();
					if (e <= 0) { SSC = ""; }
					else if (e < length) { SSC = SSC.substring(length - e); }
				}
				StringConstant = SSC;
				return true;

			case SFword:																		// WORD$
				if (!getStringArg()) { return false; }			// String to split
				String SearchString = StringConstant;

				if (!isNext(',')) { return false; }
				if (!evalNumericExpression()) { return false; }	// Which word to return
				int wordIndex = EvalNumericExpressionValue.intValue();

				String r[] = doSplit(SearchString, 0);			// Get regex arg, if any, and split the string.
				if (!isNext(')')) { return false; }				// Function must end with ')'

				length = r.length;								// Get the number of strings generated
				if (length == 0) { return false; }				// error in doSplit()

				wordIndex--;									// Convert to 0-based index
				if (r[0].length() == 0) { wordIndex++; }		// Special case: first character was a delimiter
				StringConstant = ((wordIndex < 0) || (wordIndex >= length)) ? "" : r[wordIndex];
				return true;

			case SFstr:																			// STR$
				if (!evalNumericExpression()) { return false; }
				StringConstant = String.valueOf(EvalNumericExpressionValue);
				break;

			case SFupper:																		// UPPER$
				if (!getStringArg()) { return false; }
				StringConstant = StringConstant.toUpperCase();
				break;

			case SFlower:																		// LOWER $
				if (!getStringArg()) { return false; }
				StringConstant = StringConstant.toLowerCase();
				break;

			case SFformat:																		// FORMAT $
				if (!getStringArg()) { return false; }			// get the pattern string
				if (!isNext(',')) { return false; }
				SSC = StringConstant;

				if (!evalNumericExpression()) { return false; }	// Get the number to format
				if (!isNext(')')) { return false; }				// Function must end with ')'

				return Format(SSC, EvalNumericExpressionValue);	// and then do the format

			case SFchr:																			// CHR$
				if (!evalNumericExpression()) { return false; }
				d = EvalNumericExpressionValue;
				StringConstant = "" + (char) d;
				break;
			
			case SFversion:
				StringConstant = getAllInstalledActivities(); // changed by mougino
				break;

			case SFgeterror:
				StringConstant = errorMsg;
				break;
				
			case SFreplace:
				if (!getStringArg()) { return false; }
				String target = StringConstant;

				if (!isNext(',')) { return false; }
				if (!getStringArg()) { return false; }
				String argument = StringConstant;

				if (!isNext(',')) { return false; }
				if (!getStringArg()) { return false; }
				String replacment = StringConstant;

				if (argument == null || replacment == null) {
					return RunTimeError("Invalid string");
				}
				
				StringConstant = target.replace(argument, replacment);
				break;

			case SFhex:
				if (!evalNumericExpression()) { return false; }
				e = EvalNumericExpressionValue.intValue();
				StringConstant = Integer.toHexString(e);
				break;

			case SFoct:
				if (!evalNumericExpression()) { return false; }
				e = EvalNumericExpressionValue.intValue();
				StringConstant = Integer.toOctalString(e);
				break;

			case SFbin:
				StringConstant = getDeviceName(); // changed by mougino
				break;

			default:
		}
		return isNext(')');										// Function must end with ')'

	}

  public String getDeviceName() {
    String manufacturer = Build.MANUFACTURER;
    String model = Build.MODEL;
    if (model.startsWith(manufacturer)) {
      return capitalize(model);
    } else {
      return capitalize(manufacturer) + " " + model;
    }
  }

  private String capitalize(String s) {
    if (s == null || s.length() == 0) {
      return "";
    }
    char first = s.charAt(0);
    if (Character.isUpperCase(first)) {
      return s;
    } else {
      return Character.toUpperCase(first) + s.substring(1);
    }
  } 

	private boolean parseTimeArgs(Time time) {						// Convert time parameters to Time object fields
		int year, month, day, hour, minute, second;					// Requires six parameters,
		if (!getArgAsNum()) return false;							// either numeric or string containing a number
		year = EvalNumericExpressionValue.intValue();				// Year$
		if (!isNext(',') || !getArgAsNum()) return false;
		month = EvalNumericExpressionValue.intValue() - 1;			// Month$ (convert to 0-index)
		if (!isNext(',') || !getArgAsNum()) return false;
		day = EvalNumericExpressionValue.intValue();				// Day$
		if (!isNext(',') || !getArgAsNum()) return false;
		hour = EvalNumericExpressionValue.intValue();				// Hour$
		if (!isNext(',') || !getArgAsNum()) return false;
		minute = EvalNumericExpressionValue.intValue();				// Minute$
		if (!isNext(',') || !getArgAsNum()) return false;
		second = EvalNumericExpressionValue.intValue();				// Second$
		time.set(second, minute, hour, day, month, year);
		return true;
	}

	private  boolean Format(String Fstring, double Fvalue){			// Format a number for output
																					// Do the heart of the FORMAT$ function
		BigDecimal B = BigDecimal.valueOf(0.0);
		try { B = BigDecimal.valueOf(Math.abs(Fvalue));}
		catch (Exception e) {
			return RunTimeError(e);
		}
		String Vstring =B.toPlainString();											// and convert the big decimal to a string

		String FWstring = "";  		// Will hold the whole number part of the pattern (format) string
		String FDstring = "";  		// Will hold the decimal part of the pattern string
		String VWstring = "";		// Will hold the whole number part of the number
		String VDstring = "";		// Will hold the decimal part of the number
		String Temp = "";
		int i = 0;
		char c = ' ';
		boolean FhasDecimal = false;
		int FdecimalIndex = 0;
		boolean VhasDecimal = false;
		int VdecimalIndex = 0;
		
																			// First find pattern string decimal index
		for (i=0; i< Fstring.length(); ++i){
			if (Fstring.charAt(i)== '.'){
				if (FhasDecimal){SyntaxError(); return false;}				// if more than one decimal, error
				FhasDecimal = true;
				FdecimalIndex = i;
			}
		}
		if (!FhasDecimal){													// If no decimal in pattern
			FdecimalIndex = i+1;											// set decimal index past end of pattern
			}else {															// else move index past the decimal
				++FdecimalIndex;
				}
																			// Split the pattern string
		FWstring = Fstring.substring(0, FdecimalIndex-1);									// FW is whole number part (includes decimaal)
		if (FhasDecimal) {FDstring = Fstring.substring(FdecimalIndex, Fstring.length());}	// FD is decimal string

      
        for (i=0; i<FDstring.length(); ++i){								// insure FD only has # chars
        	if (FDstring.charAt(i) != '#'){SyntaxError(); return false;}
        }
		
		for (i=0; i< Vstring.length(); ++i){								// Scan the number string for it's decimal index
			if (Vstring.charAt(i)== '.'){
				if (VhasDecimal){SyntaxError(); return false;}				// more than one decimal should never happen
				VhasDecimal = true;
				VdecimalIndex = i;											// Set the value decimal index
			}
		}
		if (!VhasDecimal){													// If value has not decimal (should never happen)
			VdecimalIndex = i+1;
			}else {
				++VdecimalIndex;											// move the decimal index past the decimal
				}
																			// Split the value string
		VWstring = Vstring.substring(0, VdecimalIndex-1);										// VW is whole number part (includes decimal)
		if (VhasDecimal) {VDstring = Vstring.substring(VdecimalIndex, Vstring.length());}		// VD is the decimal part 
																			// Build the decimal part of the output string
		Temp = "";
        if (FDstring.length()>0){Temp = ".";}								// If any pattern for decimal, output the decimal
        
        for (i=0; i<FDstring.length(); ++i){								// Copy Decimal digits to output as long as there
        	if (FDstring.charAt(i) != '#'){SyntaxError(); return false;}	// are pattern # chars. If run out of digits before
        	if (i<VDstring.length()){										// pattern digits, output 0 characters			
        		Temp = Temp + VDstring.charAt(i);
        	}else{
        		Temp = Temp + "0";
        	}
        }
        VDstring = Temp;													// Save the Decimal Value output string
        
        Temp = "";
        int FWI = FWstring.length();
        int VWI = VWstring.length();
        																	// Now work on the floating pattern
        
        String FloatChar = " ";												// Initialize float charcter to none
        if (FWI >0 &&														// If there is a float character
        	FWstring.charAt(0)!= '#' &&
        	FWstring.charAt(0)!= '%'){
        	FloatChar = Character.toString(FWstring.charAt(0));				// then move it to FloatChar
        	FWstring = FWstring.substring(1,FWI);
        	--FWI;
        	}
        
        String Header = "";													// Initialize Header to empty
        Header=Header + FloatChar;											// add the float char to the header
        if (Fvalue<0){														// if value < 0 output minus sign
        	Header = Header + "-";											// else execute space
        }else{																// note: reverse order will be unreversed later
        	Header = Header + " ";
        }

        																	// Now work on the whole number par
        StringConstant = "";
       if ( FWI ==0 && VWI >0 ){					// No Whole format characters and Whole Value characters remain
        	if (VWstring.charAt(0) != '0'){			// and the Whole character is not '0'
        		StringConstant = "*" + VDstring;	// then show error
        		return true;
        	}
            if (Fvalue<0){											// No whole number format chars and not whole number digits
            	StringConstant = StringConstant + "-";				// If the decimal digits are < 0, output minus
            }else{
            	StringConstant = StringConstant + " ";				// otherwise output blank
            }
            StringConstant = StringConstant+FloatChar+VDstring;		// Build result for decimal digits with no whole number
        	return true;											// done
        }
       												// We have whole format chars and whole value digits
        --FWI;
        --VWI;
        boolean blanks = true;
        while (FWI >=0){                                  							// While there are format characters
        	c = FWstring.charAt(FWI);					  							// get the format character
        	switch (c){

        	case '#':									  							// format charcter = #
        		blanks = true;
        		if (VWI >= 0){
        			if (VWI == 0 && VWstring.charAt(VWI) == '0'){ Temp = Temp + " ";} // if there are no more digits, output space
        			else {Temp = Temp + Character.toString(VWstring.charAt(VWI));}    // else output the digit
        			--VWI;															  // go to the next digit
        		}else if (VWI == -1){												  // No digits left, if we just ran out, output the header
        				Temp = Temp + Header + " ";									  //output the header
        				--VWI;
        				}else {
        					Temp = Temp + " ";											// output space
        					}
        		break;
 
        	case '%':																	// format charcter = %
        		blanks = false;
        		if (VWI >= 0){
        			Temp = Temp + Character.toString(VWstring.charAt(VWI));  			// if more digits, output it
        			--VWI;
        		}else
        			{Temp = Temp + "0";}									 			// else output 0
        		break;

        	default:														 			// format character not # or %
        		if (blanks){												 			// if doing blanks
        		if (VWI>=0){Temp = Temp + c;}								 			// if more digits, output char
        		else if (VWI == -1) {Temp = Temp + Header + " "; --VWI;}	 			// if just now ran out, 
        																	 			// output header and blank
        		else { Temp = Temp + " ";}									 			// else just a blank
        		} else{ Temp = Temp + c;									 			// not blanks, output the char
        		}
        	}
        	--FWI;
        }

        String t1 = "";

        if (VWI==-1){Temp = Temp + Header;}							// add the header to the end of the whole number

        for (i=Temp.length()-1; i>=0; --i){							// now reverse the whole thing
        	t1 = t1+Character.toString(Temp.charAt(i));
        	}
        Temp = t1;

        StringConstant="";
        if (VWI>=0){												// If value decimal digits remain
        	StringConstant = "**"  + Temp + VDstring;				// show the error in place of the header
        	return true;											// and we are done
        }
        
        
        StringConstant = StringConstant + Temp + VDstring;			// Build the final result
        return true;												// and we are done

	}

	private  boolean executeDIM(){									// DIM
																		// Execute a DIM Comman
		do {									// Multiple Arrays can be DIMed in one DIM statement separated by commas
			String var = getVarAndType();								// get the array name var
			if ((var == null) || !VarIsArray)	{ return RunTimeError(EXPECT_ARRAY_VAR); }
			if (!VarIsNew)						{ return RunTimeError(EXPECT_NEW_ARRAY); }
			if (isNext(']')) 					{ return false; }		// must have dimension(s)
			boolean avt = VarIsNumeric;									// preserve the array's type

			ArrayList<Integer> dimValues = new ArrayList<Integer>();	// a list to hold the array dimension values
			do {														// get each index value
				if (!evalNumericExpression())	{ return false; }
				dimValues.add(EvalNumericExpressionValue.intValue());	// and add it to the list
			} while (isNext(','));
			if (!isNext(']'))					{ return false; }		// must have closing bracket

			if (!BuildBasicArray(var, avt, dimValues)) { return false; }// no error, build the array

		} while (isNext(','));											// continue while there are arrays to be DIMed
		return checkEOL();												// then done
	}

	private boolean executeUNDIM(){
		do {									// Multiple Arrays can be UNDIMed in one Statement separated by commas
			if ((getVarAndType() == null) || !VarIsArray)	{ return RunTimeError(EXPECT_ARRAY_VAR); }
			if (!isNext(']'))								{ return RunTimeError(EXPECT_ARRAY_NO_INDEX); }
			if (!VarIsNew) {
				VarNames.set(VarNumber, " ");							// if DIMed, UNDIM it
			}
		} while (isNext(','));											// continue while there are arrays to be UNDIMed

		return checkEOL();
	}

	// ************************************* array utilities **************************************

	private boolean BuildBasicArray(String var, boolean IsNumeric, ArrayList<Integer> DimList){

		// Build a basic array attached to a new variable.
		// Makes a bundle with information about the array and put the bundle into the array table.
		// The index into the array table is put into the variable table as the the var value.
		// In addition, a value element is created for each element in the array.

		// This list of sizes is used to quickly calculate the array element offset
		// when the array is referenced.
		ArrayList<Integer> ArraySizes = new ArrayList<Integer>();

		int TotalElements = 1;
		for (int d = DimList.size() - 1; d >= 0; --d) {		// for each Dim from last to first
			int dim = DimList.get(d);						// get the Dim
			if (dim < 1) { return RunTimeError("DIMs must be >= 1 at"); }

			ArraySizes.add(0, TotalElements);				// insert the previous total in the ArraySizes List
			TotalElements= TotalElements * dim;				// multiply this dimension by the previous size

			if (TotalElements > 50000) {					// Limit the size of any one array
				return RunTimeError("Array exceeds 50,000 elements");	// to 50,000 elements
			}
		}

		if (IsNumeric) {									// Initialize Numeric Array Values
			ArrayValueStart = NumericVarValues.size();		// All numeric var values kept in NumericVarValues
			for (int i = 0; i < TotalElements; ++i) {		// Number inited to 0.0
				NumericVarValues.add(0.0);
			}
		} else {											// Initialize String Array Values
			ArrayValueStart = StringVarValues.size();		// All string var values kept in StringVarValues
			for (int i = 0; i < TotalElements; ++i) {
				StringVarValues.add("");					// Strings inited to empty
			}
		}

		Bundle ArrayEntry = new Bundle();						// Build the array table entry bundle
		ArrayEntry.putIntegerArrayList("dims", DimList);		// The array index values
		ArrayEntry.putIntegerArrayList("sizes", ArraySizes);	// The array sizes
		ArrayEntry.putInt("length", TotalElements);
		ArrayEntry.putInt("base", ArrayValueStart);				// The pointer the first array element value

		int varNum = createNewVar(var);
		VarIndex.set(varNum, ArrayTable.size());				// The var's value is its array table index
		ArrayTable.add(ArrayEntry);								// Put the element bundle into the array table

		return true;
	} // end BuildBasicArray

	private boolean BuildBasicArray(String var, boolean IsNumeric, int length) { // Build 1D array
		ArrayList <Integer> dimValues = new ArrayList<Integer>();		// list of dimensions
		dimValues.add(length);											// only one dimension
		return (BuildBasicArray(var, IsNumeric, dimValues));			// go build an array of the proper size
	}

	private boolean ListToBasicNumericArray(String var, List <Double> Values, int length) {
		if (!BuildBasicArray(var, true, length)) return false;			// go build an array of the proper size and type
		int i = ArrayValueStart;
		for (Double d : Values) {										// stuff the array
			NumericVarValues.set(i++, d);
		}
		return true;
	}

	private boolean ListToBasicStringArray(String var, List <String> Values, int length) {
		if (!BuildBasicArray(var, false, length)) return false;			// go build an array of the proper size and type
		int i = ArrayValueStart;
		for (String s : Values) {										// stuff the array
			StringVarValues.set(i++, s);
		}
		return true;
	}

	private boolean GetArrayValue() {				// Get the value of an array element using its index values
		ArrayList<Integer> indicies = new ArrayList<Integer>();
		if (!isNext(']')) {							// Parse out the index values for this call
			int avn = VarNumber;					// preserve the array's VarNumber
			boolean avt = VarIsNumeric;				// and type
			do {
				if (!evalNumericExpression())	{ SyntaxError(); return false; }
				indicies.add(EvalNumericExpressionValue.intValue());
			} while (isNext(','));
			if (!isNext(']'))					{ SyntaxError(); return false; }
			VarNumber = avn;						// restore the array's varNumber
			VarIsNumeric = avt;						// and type
		}

		Bundle ArrayEntry = ArrayTable.get(VarIndex.get(VarNumber)); // Get the array table bundle for this array
		ArrayList<Integer> dims = ArrayEntry.getIntegerArrayList("dims");
		ArrayList<Integer> sizes = ArrayEntry.getIntegerArrayList("sizes");

		if (dims.size() != indicies.size()) {		// insure index count = dim count
			RunTimeError(
					"Indices count(" +
					indicies.size()+
					") not same as dimension count ("+
					dims.size()+
					") at:");
			return false;
		}
		
		int offset = 0;
		
		for (int i=0; i<indicies.size(); ++i){					
			int p = indicies.get(i);						// p = index for this call
			int q = dims.get(i);							// q = DIMed value for this index
			int r = sizes.get(i);							// r = size for this index
			if (p>q){
				RunTimeError("Index #"+
						(i+1) +
						" (" +
						p +
						") exceeds dimension (" +
						q +
						") at:");		// insure Index <= DIMed index
				return false;
			}
			if (p<=0){											// insure index >= 1
				RunTimeError("Index must be >=1 at:");
				return false;
			}
			offset = offset + (p-1)*r;							// calculate offset
		}
		
		int base = ArrayEntry.getInt("base");            // base + offset gives
		theValueIndex = base + offset;					 // displacement into value table for
		return true;									 // index combination
		
	}

	private boolean getIndexPair(Integer[] pair) {					// get contents of [] from command line
																	// at most two index values accepted
		boolean isBracket = isNext(']');
		if (!isBracket) {
			boolean isComma = isNext(',');
			if (!isComma) {
				if (!evalNumericExpression()) return false;
				pair[0] = EvalNumericExpressionValue.intValue();	// first index
				isComma = isNext(',');
			}
			isBracket = isNext(']');
			if (!isBracket && isComma) {
				if (!evalNumericExpression()) return false;
				pair[1] = EvalNumericExpressionValue.intValue();	// second index
				isBracket = isNext(']');
			}
		}
		return (isBracket);											// must end with ']'
	}

	private boolean getArraySegment(int arrayTableIndex, Integer[] pair) { // get var base and length of array segment
																	// pair in is [start, length], out is [base, length]
		Bundle b = ArrayTable.get(arrayTableIndex);					// get the array table bundle for this array
		if (b == null) { return RunTimeError("Array does not exist"); }
		int length = b.getInt("length");							// get the array length
		int base = b.getInt("base");								// and the start of the array in the variable space
		int max = base + length;

		if (pair[0] != null) {
			int start = pair[0].intValue();							// start index, 1-based, default 1
			if (start > 1) { base += start - 1; }					// convert to 0-based index, ignore if less than 1
			if (base > max) { base = max; }							// not an error, just force length to 0
		}
		if (pair[1] != null) {
			length = pair[1].intValue();							// requested length
			if (length < 0) { length = 0; }							// eliminate negative input
		}
		if ((base + length) > max) { length = max - base; }			// don't go off end of array

		pair[0] = base;
		pair[1] = length;
		return true;
	}

	// ************************************* end array utilities **************************************

	private boolean executePRINT(){
		if (!buildPrintLine(PrintLine, "")) return false;	// build up the print line in StringConstant
		if (!PrintLineReady) {							// flag set by buildPrintLine
			PrintLine = StringConstant;					// not ready to print; hold line
			return true;								// and wait for next Print command
		}
		PrintLine = "";									// clear the accumulated print line
		PrintShow(StringConstant);						// then output the line
		return true;
	}

	// Convert the fields of a print command into a String for printing.
	// The line param can hold an existing String to add the new String to.
	// If the line ends with a semicolon set PrintLineReady false,
	// else add the newline param to the String and set PrintLineReady true.
	// If the String is valid, put it in StringConstant and return true,
	// else return false to signal a syntax error.
	private boolean buildPrintLine(String line, String newline){
		StringBuilder printLine = new StringBuilder((line == null) ? "" : line);
		boolean WasSemicolon = false;
		do {										// do each field in the print statement
			int LI = LineIndex;
			if (evalNumericExpression()) {
				printLine.append(EvalNumericExpressionValue);	// convert to string
				WasSemicolon = false;
			} else {
				if (evalStringExpression()) {
					printLine.append(StringConstant);			// field is string
					WasSemicolon = false;
				} else {
					if (VarIsFunction) { return false; }
					if (LI == LineIndex) {						// if no more fields, append newline
						if (!checkEOL()) { return false; }		// unless there is junk on the line
					}
				}
			}
			if (SyntaxError) { return false; }

			char c = ExecutingLineBuffer.charAt(LineIndex);
			if (c == ',') {							// if the separator is a comma
				printLine.append(", ");				// add comma + blank to the line
				++LineIndex;
			} else if (c == ';') {					// if separator is semicolon
													// don't add anything to output
				WasSemicolon = true;				// and signal we had a semicolon
				c = ExecutingLineBuffer.charAt(++LineIndex);	// is next char eol?
			}

			if (c == '\n') {						// Done processing the line
				if (!WasSemicolon) {				// if not ended in semicolon
					printLine.append(newline);		// add newline character(s)
				}
				break;
			}
		} while (true);								// Exit loop happens internal to loop
		
		PrintLineReady = !WasSemicolon;
		StringConstant = printLine.toString();
		return true;
	}

	private boolean executeEND() {

		Stop = true;								// ALWAYS stop

		String endMsg = "END";						// Default END message
		boolean ok = true;
		if (!isEOL()) {
			ok = getStringArg();					// Get user's END message
			if (ok) {
				endMsg = StringConstant;
				ok = checkEOL();
			}
		}

		if (endMsg.length() > 0) { PrintShow(endMsg); }
		return ok;

	}

	private int getLabelLineNum() {
		String label = getWord(ExecutingLineBuffer, LineIndex, false);	// get label name
		int len = label.length();
		LineIndex += len;
		if (len != 0) {
			Integer lineNum = Labels.get(label);
			if (lineNum == null) {
				RunTimeError("Undefined Label at:");
			} else {
				return lineNum.intValue();
			}
		}
		return -1;
	}

	private boolean executeGOTO() {

		int maxStack = 50000 ;						// 50,000 should be enough

		if ((IfElseStack.size() > maxStack) || (ForNextStack.size() > maxStack)) {
			return RunTimeError("Stack overflow. See manual about use of GOTO.");
		}

		int gln = getLabelLineNum();
		if ((gln < 0) || !checkEOL()) return false;
		ExecutingLineIndex = gln;
		return true;
	}

	private boolean executeGOSUB() {
		int gln = getLabelLineNum();
		if ((gln < 0) || !checkEOL()) return false;
		GosubStack.push(ExecutingLineIndex);
		ExecutingLineIndex = gln;
		return true;
	}

	private  boolean executeIF(){
		
		if (!IfElseStack.empty()){			 								// if inside of an IF block		
			Integer q = IfElseStack.peek();
			if ((q != IEexec) && (q != IEinterrupt)) {						// and not executing
				int index = ExecutingLineBuffer.indexOf("then");
				if (index < 0) {											// if there is no THEN
					IfElseStack.push(IEskip2);								// need to skip to this if's end
				} else {
					LineIndex = index + 4;									// skip over the THEN
					if (isNext('\n')) {      								// if not single line if
						IfElseStack.push(IEskip2);							// need to skip to this if's end
					}														// else is single line, no skip needed
				}
				return true;
			}
		}
		// Execute this IF
		
		PossibleKeyWord = "then";					// Tell parseVar to expect a THEN
		if (!evalNumericExpression()) { return false; }
		PossibleKeyWord = "";						// parseVar should no longer expect THEN
		boolean condition = (EvalNumericExpressionValue != 0);
		
		if (ExecutingLineBuffer.charAt(LineIndex) != '\n') {
			if (!ExecutingLineBuffer.startsWith("then", LineIndex)) { checkEOL(); return false; }
			LineIndex = LineIndex + 4;

			if (!isNext('\n')) { return SingleLineIf(condition); }			// assume single-line IF

			// at this point: "IF condition THEN\n" and LineIndex is after '\n'
		}
		
		IfElseStack.push((condition) ? IEexec : IEskip1);

		return true;
	}
	
	private boolean SingleLineIf(boolean condition){
		int index = ExecutingLineBuffer.lastIndexOf("else");
		
		if (condition) {
			if (index > 0 ) {
				ExecutingLineBuffer = ExecutingLineBuffer.substring(0, index) + "\n";
			}
			return StatementExecuter();
		}

		if (index > 0) {
			LineIndex = index + 4;
			return StatementExecuter();
		}
		return true;
	}
	
	private  boolean executeELSE(){
		
		if (IfElseStack.empty()) {			// prior IF or ELSEIF should have put something on the stack
			RunTimeError("Misplaced ELSE");
			return false;
		}
		if (!checkEOL() ) return false;

		Integer q = IfElseStack.pop();
		IfElseStack.push((q == IEexec) ? IEskip2 : IEexec);

		return true;
	}
	
	private boolean executeELSEIF(){
		
		if (IfElseStack.empty()) {			// prior IF or ELSEIF should have put something on the stack
			RunTimeError("Misplaced ELSEIF");
			return false;
		}
		
		Integer q = IfElseStack.pop();

		if (q == IEexec) {
			IfElseStack.push(IEskip2);
		} else {
			PossibleKeyWord = "then";					// Tell parseVar to expect a THEN
			if (!evalNumericExpression()) { return false; }
			PossibleKeyWord = "";						// parseVar should no longer expect THEN
			boolean condition = (EvalNumericExpressionValue != 0);

			if (ExecutingLineBuffer.startsWith("then", LineIndex)) LineIndex = LineIndex + 4;
			if (!checkEOL()) { return false; }

			IfElseStack.push((condition) ? IEexec : IEskip1);
		}
		return true;
	}
	
		private boolean executeENDIF(){
			
			if (IfElseStack.empty()){			// Something must be on the stack
				RunTimeError("Misplaced ENDIF");
				return false;
			}
			if (!checkEOL() ) return false;
			IfElseStack.pop();					// but we do not care what it is
			return true;

		}

	private boolean skipTo(String target, String nest, String errMsg) {
		int lineNum;
		int limit = Basic.lines.size();
		for (lineNum = ExecutingLineIndex + 1; lineNum < limit; ++lineNum) {
			String line = Basic.lines.get(lineNum);
			if (line.startsWith(target)) {
				ExecutingLineIndex = lineNum;					// found the target
				ExecutingLineBuffer = line;
				LineIndex = target.length();
				return true;
			}
			if (line.startsWith(nest)) {
				ExecutingLineIndex = lineNum;					// found nested block of same type
				if (!skipTo(target, nest, errMsg)) return false;// recursively seek its end
				lineNum = ExecutingLineIndex;
			}
		}
		return RunTimeError(errMsg);							// end of program, target not found
	}

	private boolean executeFOR(){

		Bundle b = new Bundle();						// A bundle to hold value for stack

		b.putInt("line",ExecutingLineIndex);							// Loop return location

		if (!getNVar()) { return false; }
		b.putInt("var", theValueIndex);										// For Var
		int IndexValueIndex = theValueIndex;
		
		if (!isNext('=')) { return false; }									// For Var =

		PossibleKeyWord = "to";						// Tell get var that a TO is expected
		if (!evalNumericExpression()) { return false; }						// for Var = <exp>
		PossibleKeyWord = "";
		double fstart = EvalNumericExpressionValue;
		NumericVarValues.set(IndexValueIndex, fstart);   // assign <exp> to Var

		if (!ExecutingLineBuffer.startsWith("to", LineIndex)) return false;
		LineIndex = LineIndex +2;
		
		PossibleKeyWord = "step";
		if (!evalNumericExpression()) { return false; }						// For Var = <exp> to <exp>
		PossibleKeyWord = "";
		b.putDouble("limit", EvalNumericExpressionValue);
		double flimit = EvalNumericExpressionValue;

		double step = 1.0;													//For Var = <exp> to <exp> <default step> 
		if (ExecutingLineBuffer.startsWith("step", LineIndex)){
			LineIndex = LineIndex + 4;
			if (!evalNumericExpression()) { return false; }					// For Var = <exp> to <exp> step <exp>
			step = EvalNumericExpressionValue;
		}
		
		if (!checkEOL()) return false;
		
		b.putDouble("step", step);
		
		if (step > 0) {											// Test the initial condition
			if (fstart > flimit) {return SkipToNext();}			// If exceeds limit then skip to NEXT
		} else {
			if (fstart < flimit) {return SkipToNext();}
		}
		ForNextStack.push(b);
		
		return true;
	}

	private boolean SkipToNext() {
		return skipTo("next", "for", "FOR without NEXT");
	}

	private boolean executeF_N_CONTINUE(){
		if (ForNextStack.empty()) {								// If the stack is empty
			return RunTimeError("No For Loop Active");			// then we have a misplaced CONTINUE
		}
		if (!checkEOL()) return false;

		if (!SkipToNext()) return false;
		doNext();
		return true;
	}

	private boolean executeF_N_BREAK(){
		if (ForNextStack.empty()) {								// If the stack is empty
			return RunTimeError("No For Loop Active");			// then we have a misplaced BREAK
		}
		if (!checkEOL()) return false;

		if (!SkipToNext()) return false;
		ForNextStack.pop();
		return true;
	}

	private boolean executeNEXT(){
		if (ForNextStack.empty()) {								// If the stack is empty
			return RunTimeError("NEXT without FOR");			// then we have a misplaced NEXT
		}
		return doNext();
	}

	private boolean doNext() {
		Bundle b = ForNextStack.peek();							// Peek at the TOS Bundle
		int line = b.getInt("line");
		int varindex = b.getInt("var");							// We did not store the index value, we stored a pointer to it
		double var = NumericVarValues.get(varindex);
		double limit = b.getDouble("limit");
		double step = b.getDouble("step");
		
		var += step;											// Do the STEP
		NumericVarValues.set(varindex, var);					// Assign the result to the index
		
		if (((step > 0) && (var <= limit)) || ((step <= 0) && (var >= limit))) { // Test limit
			ExecutingLineIndex = line;
		} else {
			ForNextStack.pop();									// If done, pop the stack
		}
		return true;
	}

	private boolean executeWHILE(){
		if (!evalNumericExpression()) return false;
		if (!checkEOL()) return false;

		if (EvalNumericExpressionValue != 0) {
			WhileStack.push(ExecutingLineIndex - 1);			// true: push line number onto while stack
			return true;
		}
		return SkipToRepeat();									// false: find the REPEAT for the WHILE
	}

	private boolean SkipToRepeat() {
		return skipTo("repeat", "while", "WHILE without REPEAT");
	}

	private boolean executeW_R_CONTINUE(){
		if (WhileStack.empty()) {								// If the stack is empty
			return RunTimeError("No While Loop Active");		// then we have a misplaced CONTINUE
		}
		if (!checkEOL()) return false;

		ExecutingLineIndex = WhileStack.pop();					// Pop the line number of the WHILE
		return true;
	}

	private boolean executeW_R_BREAK(){
		if (WhileStack.empty()) {								// If the stack is empty
			return RunTimeError("No While Statement Active");	// then we have a misplaced BREAK
		}
		if (!checkEOL()) return false;

		if (!SkipToRepeat()) return false;
		WhileStack.pop();
		return true;
	}

	private boolean executeREPEAT(){
		if (WhileStack.empty()) {								// If the stack is empty
			return RunTimeError("REPEAT without WHILE");		// then we have a misplaced REPEAT
		}
		if (!checkEOL()) return false;

		ExecutingLineIndex = WhileStack.pop();					// Pop the line number of the WHILE
		return true;
	}

	private boolean executeDO(){
		if (!checkEOL()) return false;
		DoStack.push(ExecutingLineIndex - 1);					// push line number onto DO stack.
		return true;
	}

	private boolean SkipToUntil() {
		return skipTo("until", "do", "DO without UNTIL");
	}

	private boolean executeD_U_CONTINUE(){
		if (DoStack.empty()) {									// If the stack is empty
			return RunTimeError("No DO loop active");			// then we have a misplaced CONTINUE
		}
		if (!checkEOL()) return false;

		if (!SkipToUntil()) return false;
		return doUntil();
	}

	private boolean executeD_U_BREAK(){
		if (DoStack.empty()) {									// If the stack is empty
			return RunTimeError("No DO loop active");			// then we have a misplaced BREAK
		}
		if (!checkEOL()) return false;

		if (!SkipToUntil()) return false;
		DoStack.pop();
		return true;
	}

	private boolean executeUNTIL(){
		if (DoStack.empty()) {									// If the stack is empty
			return RunTimeError("UNTIL without DO");			// then we have a misplaced UNTIL
		}
		return doUntil();
	}

	private boolean doUntil() {
		if (!evalNumericExpression()) return false;
		if (!checkEOL()) return false;

		if (EvalNumericExpressionValue == 0) {
			ExecutingLineIndex = DoStack.pop();					// false: pop the DO line number and go to it.
		} else {
			DoStack.pop();										// true: pop the stack
		}
		return true;
	}

	private boolean executeINPUT(){

		if (!getStringArg()) return false;
		UserPrompt = StringConstant;
		if (!isNext(',')) return false;
		for (int i = UserPrompt.length(); i < 19 ; ++i) {
			UserPrompt = UserPrompt + (" ");
		}
		if (!getVar()) return false;
		InputIsNumeric = VarIsNumeric;
		InputVarIndex = theValueIndex;

		InputDefault = "";
		if (isNext(',')) {
			if (InputIsNumeric) {
				if (!evalNumericExpression()) return false;
				StringConstant = String.valueOf(EvalNumericExpressionValue);
			} else {
				if (!getStringArg()) return false;
			}
			InputDefault = StringConstant;
		}
		if (!checkEOL()) return false;

		WaitForInput = true;
		BadInput = false;
		InputCancelled = false;
		PrintShow("@@1");
		return true;
	}

	private AlertDialog.Builder doInputDialog(){
		Context context = getContext();
		AlertDialog.Builder dialog = new AlertDialog.Builder(context);
		final EditText input = new EditText(context);
		input.setText(InputDefault);
		if (InputIsNumeric) input.setInputType(0x00003002); // Limits keys to signed decimal numbers
		dialog.setView(input);
		dialog.setCancelable(true);
		dialog.setTitle(UserPrompt);
		dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
			public void onCancel(DialogInterface arg0) {
				InputCancelled = true;
			}
	    });
	    dialog.setPositiveButton("Ok", new DialogInterface.OnClickListener() {

// ****************************************  start of Dialog Click Listener**************************************
//
//  This method is not executed until the user presses OK on the Dialog Box

			public void onClick(DialogInterface dialog, int whichButton) {
				String theInput = input.getText().toString();
				if (InputIsNumeric) {							// Numeric Input Handling
					double d = 0;
					try {
						d = Double.parseDouble(theInput.trim());// have java parse it into a double
					} catch (Exception e) {
						BadInput = true;
						return;
					}
					NumericVarValues.set(InputVarIndex, d);
				} else {										// String Input Handling
					StringVarValues.set(InputVarIndex, theInput);
				}
				WaitForInput = false;
				theAlertDialog = null;
			}});
//***************End of Input Click Listener *******************************************

		return dialog;
	}
	
	private boolean executeBACK_RESUME(){
		if (interruptResume == -1){
			RunTimeError("Back key not hit");
			return false;
		}
		return doResume();
	}
	
	private boolean executeONMENUKEY_RESUME(){
		if (interruptResume == -1){
			RunTimeError("Menu key not hit");
			return false;
		}
		return doResume();
	}
	
	private boolean executeCONSOLE_RESUME(){
		if (interruptResume == -1){
			RunTimeError("Console not touched");
			return false;
		}
		return doResume();
	}
	
	private boolean doResume(){
		ExecutingLineIndex = interruptResume;
		interruptResume = -1;
		VarSearchStart = interruptVarSearchStart;
		// Pull the IEinterrupt from the If Else stack
		// It is possible that IFs were executed in the interrupt code
		// so pop entries until we get to the IEinterrupt
		while (IfElseStack.peek() != IEinterrupt) {
			IfElseStack.pop();
		}
		IfElseStack.pop();  // Top is stack is now IEInterrupt, pop it
				
		return true;
	}
	
// ************************************************** Read Commands **********************************
	
	// Parse and bundle the data list of a READ.DATA statement
	// Called from PreScan() and NOT from statementExecuter().
	private boolean executeREAD_DATA() {
		do {													// Sweep up the data values
			Bundle b = new Bundle();
			if (GetStringConstant()) {							// If it is a string
				b.putBoolean("isNumber", false);				// Create a bundle for it
				b.putString("string", StringConstant);
			}
			else {												// Should be a number
				double signum = 1.0;							// Assume positive
				if (isNext('-')) { signum = -1.0; }				// Catch minus sign
				else if (isNext('+')) { ; }						// If not negative, eat optional '+'

				if (getNumber()) {								// If it is a number
					b.putBoolean("isNumber", true);				// Create a bundle for it
					b.putDouble("number", signum * GetNumberValue);
				}
				else {											// Else is a run time error
					RunTimeError("Invalid Data Value");
					return false;
				}
			}
			readData.add(b);									// Add the bundle to the list
		} while (isNext(','));									// and do again if more data

		return checkEOL();										// Nothing allowed after scan stops
	}

	private boolean executeREAD_NEXT(){
		
		do {
		
			if (readNext >= readData.size()) {					// Insure there is more data to read
				RunTimeError("No more data to read");
				return false;
			}
		
			Bundle b = readData.get(readNext);					// Get the data bundle
			++readNext;											// And increment to next bundle
			
			if (!getVar()) return false;						// Get the variable
		
			if (VarIsNumeric) {									// If var is numeric
				if (!b.getBoolean("isNumber")) {				// Insure data is a number
					RunTimeError("Data type (String) does match variable type (Number)");
					return false;
				}
				double d = b.getDouble("number");				// Get the number
				NumericVarValues.set(theValueIndex, d );		// and put it into the variable
				
			}else {												// else var is string
				if (b.getBoolean("isNumber")) {					// Insure data is a string
					RunTimeError("Data type (Number) does match variable type (String)");
					return false;
				}
				String s = b.getString("string");				// Get the string
				StringVarValues.set(theValueIndex, s );			// and put it into the variable
			}
		
		} while (isNext(','));									// loop while there are variables
		
		return checkEOL();
	}
	
	
	private boolean executeREAD_FROM(){
		if (!evalNumericExpression()) return false;
		int newIndex = EvalNumericExpressionValue.intValue();
		--newIndex;
		if (newIndex < 0 || newIndex >= readData.size()) {
			RunTimeError("Index out of range");
			return false;
		}
		
		readNext = newIndex;
		
		return checkEOL();
	}


// **************************************************  Debug Commands *********************************

	private boolean executeDEBUG(){

		if (!GetDebugKeyWord()) return false;

		switch (KeyWordValue){

			case debug_on:
				if (!executeDEBUG_ON()){SyntaxError(); return false;}
				break;
			case debug_off:
				if (!executeDEBUG_OFF()){SyntaxError(); return false;}
				break;
			case debug_print:
				if (!executeDEBUG_PRINT()){SyntaxError(); return false;}
				break;
			case debug_echo_on:
				if (!executeECHO_ON()){SyntaxError(); return false;}
				break;				
			case debug_echo_off:
				if (!executeECHO_OFF()){SyntaxError(); return false;}
				break;
			case debug_dump_scalars:
				if (!executeDUMP_SCALARS()){SyntaxError(); return false;}
				break;
			case debug_dump_array:
				if (!executeDUMP_ARRAY()){SyntaxError(); return false;}
				break;
			case debug_dump_list:
				if (!executeDUMP_LIST()){SyntaxError(); return false;}
				break;
			case debug_dump_stack:
				if (!executeDUMP_STACK()){SyntaxError(); return false;}
				break;
			case debug_dump_bundle:
				if (!executeDUMP_BUNDLE()){SyntaxError(); return false;}
				break;
			case debug_watch_clear:
				if (!executeDEBUG_WATCH_CLEAR()){SyntaxError(); return false;}
				break;
			case debug_watch:
				if(!executeDEBUG_WATCH()){SyntaxError(); return false;}
				break;
			case debug_show_scalars:
				if (!executeDEBUG_SHOW_SCALARS()){SyntaxError(); return false;}
				break;
			case debug_show_array:
				if (!executeDEBUG_SHOW_ARRAY()){SyntaxError(); return false;}
				break;
			case debug_show_list:
				if (!executeDEBUG_SHOW_LIST()){SyntaxError(); return false;}
				break;
			case debug_show_stack:
				if (!executeDEBUG_SHOW_STACK()){SyntaxError(); return false;}
				break;
			case debug_show_bundle:
				if (!executeDEBUG_SHOW_BUNDLE()){SyntaxError(); return false;}
				break;
			case debug_show_watch:
				if (!executeDEBUG_SHOW_WATCH()){SyntaxError(); return false;}
				break;
			case debug_show_program:
				if (!executeDEBUG_SHOW_PROGRAM()){SyntaxError();return false;}
				break;
			case debug_show:
				if (!executeDEBUG_SHOW()){SyntaxError(); return false;}
				break;
			case debug_console:
				if (!executeDEBUG_CONSOLE()){SyntaxError();return false;}
				break;
			default:
		}

		return true;
	}

	private boolean GetDebugKeyWord(){						// Get a debug command keyword if it is there
															// is the current line index at a keyword?
		for (int i = 0; i < Debug_KW.length; ++i) {			// loop through the keyword list
			if (ExecutingLineBuffer.startsWith(Debug_KW[i], LineIndex)) { // if there is a match
				KeyWordValue = i;							// set the keyword number
				LineIndex += Debug_KW[i].length();			// move the line index to end of keyword
				return true;								// and report back
			}
		}
		KeyWordValue = debug_none;							// no keyword found
		return false;										// report fail
	}

	private boolean executeDEBUG_ON() {
		if (!checkEOL()) return false;
		Debug = true;
		return true;
	}

	private boolean executeDEBUG_OFF() {
		if (!checkEOL()) return false;
		Debug = false;
		Echo = false;
		return true;
	}

	private boolean executeDEBUG_PRINT() {
		if (Debug) executePRINT();
		return true;
	}

	private boolean executeECHO_ON(){
		if (!checkEOL()) return false;
		if (Debug) Echo = true;
		return true;
	}

	private boolean executeECHO_OFF(){
		if (!checkEOL()) return false;
		Echo = false;
		return true;
	}

	private boolean executeDUMP_SCALARS(){
		if (!Debug) return true;
		if (!checkEOL()) return false;

		ArrayList<String> lines = dbDoScalars("");
		for (String line : lines) {
			if (line != null) { PrintShow(line); }
		}
		PrintShow("....");
		return true;
	}

	private boolean executeDUMP_ARRAY(){
		if (!Debug) return true;

		String var = getVarAndType();
		if ((var == null) || !VarIsArray)	{ return RunTimeError(EXPECT_ARRAY_VAR); }
		if (VarIsNew)						{ return RunTimeError(EXPECT_DIM_ARRAY); }
		// No checkEOL: ignore anything after the '['

		WatchedArray = VarNumber;
		ArrayList<String> lines = dbDoArray("");
		for (String line : lines) {
			if (line != null) { PrintShow(line); }
		}
		PrintShow("....");
		return true;
	}

	private boolean executeDUMP_LIST(){
		if (!Debug) return true;

		int listIndex = getListArg();							// get the list pointer
		if (listIndex == 0) return false;
		if (!checkEOL()) return false;

		WatchedList = listIndex;
		ArrayList<String> lines = dbDoList("");
		for (String line : lines) {
			if (line != null) { PrintShow(line); }
		}
		PrintShow("....");
		return true;
	}

	private boolean executeDUMP_STACK(){
		if (!Debug) return true;

		int stackIndex = getStackIndexArg();					// get the stack pointer
		if (stackIndex == 0) return false;
		if (!checkEOL()) return false;

		WatchedStack = stackIndex;
		ArrayList<String> lines = dbDoList("");
		for (String line : lines) {
			if (line != null) { PrintShow(line); }
		}
		PrintShow("....");
		return true;
	}

	private boolean executeDUMP_BUNDLE(){
		if (!Debug) return true;

		int bundleIndex = getBundleArg();						// get the Bundle pointer
		if (bundleIndex == 0) return false;
		if (!checkEOL()) return false;

		WatchedBundle = bundleIndex;
		ArrayList<String> lines = dbDoBundle("");
		for (String line : lines) {
			if (line != null) { PrintShow(line); }
		}
		PrintShow("....");
		return true;
	}

	//=====================DEBUGGER DIALOG STUFF========================

	private boolean executeDEBUG_WATCH_CLEAR(){
		if(!Debug) return true;
		if (!checkEOL()) return false;

		WatchVarIndex.clear();
		Watch_VarNames.clear();
		return (WatchVarIndex.isEmpty() && Watch_VarNames.isEmpty());
	}

	private boolean executeDEBUG_WATCH(){				// separate the names and store them
		if (!Debug) return true;

		int max = ExecutingLineBuffer.length() - 1;
		int ni = LineIndex;								// start of name string
		do {
			int i = ExecutingLineBuffer.indexOf(',', ni);
			if (i < 0) { i = max; }
			String name = ExecutingLineBuffer.substring(ni, i);
			getVar();
			boolean add = true;
			for (int j = 0; j < WatchVarIndex.size(); ++j) {
				if (WatchVarIndex.get(j)==VarNumber) { add = false; }
			}
			if (add) {
				Watch_VarNames.add(name);
				WatchVarIndex.add(VarNumber);
			}
			LineIndex = ni = i + 1;
		} while (ni < max);
		return true;
	}

	private boolean executeDEBUG_SHOW_SCALARS(){
		DialogSelector(1);
		executeDEBUG_SHOW();
		return true;
	}

	private boolean executeDEBUG_SHOW_ARRAY(){
		if (!Debug) return true;

		String var = getVarAndType();
		if ((var == null) || !VarIsArray)	{ return RunTimeError(EXPECT_ARRAY_VAR); }
		if (VarIsNew)						{ return RunTimeError(EXPECT_DIM_ARRAY); }

		WatchedArray = VarNumber;
		DialogSelector(2);
		executeDEBUG_SHOW();
		return true;
	}

	private boolean executeDEBUG_SHOW_LIST(){
		if (!Debug) return true;

		int listIndex = getListArg();							// get the list pointer
		if (listIndex == 0) return false;

		WatchedList = listIndex;
		DialogSelector(3);
		executeDEBUG_SHOW();
		return true;
	}

	private boolean executeDEBUG_SHOW_STACK(){
		if (!Debug) return true;

		int stackIndex = getStackIndexArg();					// get the stack pointer
		if (stackIndex == 0) return false;

		WatchedStack = stackIndex;
		DialogSelector(4);
		executeDEBUG_SHOW();
		return true;
	}

	private boolean executeDEBUG_SHOW_BUNDLE(){
		if (!Debug) return true;

		int bundleIndex = getBundleArg();						// get the Bundle pointer
		if (bundleIndex == 0) return false;

		WatchedBundle = bundleIndex;
		DialogSelector(5);
		executeDEBUG_SHOW();
		return true;
	}

	private boolean executeDEBUG_SHOW_WATCH(){
		if (!Debug) return true;
		DialogSelector(6);
		executeDEBUG_SHOW();
		return true;
	}

	private boolean executeDEBUG_CONSOLE(){
		if (!Debug) return true;
		DialogSelector(7);
		executeDEBUG_SHOW();
		return true;
	}

	private boolean executeDEBUG_SHOW_PROGRAM(){
		if(!Debug) return true;
		DialogSelector(8);
		executeDEBUG_SHOW();
		return true;
	}

			private void DialogSelector(int selection){
				dbDialogScalars = false;
				dbDialogArray = false;
				dbDialogList = false;
				dbDialogStack = false;
				dbDialogBundle = false;
				dbDialogWatch = false;
				dbDialogConsole = false;
				dbDialogProgram = false;
				switch (selection){
				  case 1:
					  dbDialogScalars = true;
					  break;
					case 2:
				  	dbDialogArray = true;
					  break;
					case 3:
					  dbDialogList = true;
				  	break;
					case 4:
				  	dbDialogStack = true;
				  	break;
					case 5:
				  	dbDialogBundle = true;
					  break;
					case 6:
					  dbDialogWatch = true;
					  break;
					case 7:
				  	dbDialogConsole = true;
				  	break;
					default:
					  dbDialogProgram = true;
						break;
				}
			}

	private boolean executeDEBUG_SHOW() {				// trigger do debug dialog
		if (!Debug) return true;
		WaitForResume = true;
		sendMessage(MESSAGE_DEBUG_DIALOG);
		return true;
	}

	private String chomp(String str) {
		return str.substring(0, str.length() - 1);
	}

	private String quote(String str) {
		return '\"' + str + '\"';
	}

	private void doDebugDialog() {

		ArrayList<String> msg = new ArrayList<String>();

		if (!dbDialogProgram) {
			msg = dbDoFunc();
			msg.add("Executable Line #:    " + Integer.toString(ExecutingLineIndex + 1)
					+ '\n' + chomp(ExecutingLineBuffer));
		}

		if (dbDialogScalars) msg.addAll(dbDoScalars("  "));
		if (dbDialogArray)   msg.addAll(dbDoArray("  "));
		if (dbDialogList)    msg.addAll(dbDoList("  "));
		if (dbDialogStack)   msg.addAll(dbDoStack("  "));
		if (dbDialogBundle)  msg.addAll(dbDoBundle("  "));
		if (dbDialogWatch)   msg.addAll(dbDoWatch("  "));
 
		if (dbDialogProgram) {
			for (int i = 0; i < Basic.lines.size(); ++i) {
				msg.add(((i == ExecutingLineIndex) ? " >>" : "   ")	// mark current line
						+ (i + 1) + ": "							// one-based line index
						+ chomp(Basic.lines.get(i)));				// remove newline
			}
		}

		LayoutInflater inflater = getLayoutInflater();
		View dialogLayout = inflater.inflate(R.layout.debug_dialog_layout, null);

		ListView debugView = (ListView)dialogLayout.findViewById(R.id.debug_list); 
		debugView.setAdapter(new ArrayAdapter<String>(this, R.layout.debug_list_layout, msg));
		debugView.setVerticalScrollBarEnabled(true);
		if (dbDialogProgram) { debugView.setSelection(ExecutingLineIndex); }

		AlertDialog.Builder builder = new AlertDialog.Builder(this)
			.setCancelable(true)
			.setTitle(R.string.debug_name)
			.setView(dialogLayout);

		builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
			public void onCancel(DialogInterface arg0) {
				DebuggerHalt = true;
				WaitForResume = false;
			}
		});

		builder.setPositiveButton("Resume", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog,int which) {
				WaitForResume = false;
			}
		});

		builder.setNeutralButton("Step", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog,int which){
				DebuggerStep = true;
				WaitForResume = true;
			}
		});

		// leave out until the switcher is done.
		builder.setNegativeButton("View Swap",new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog,int which) {
				dbSwap = true;
			}
		});

		dbDialog = builder.show();
		dbDialog.getWindow().setLayout(WindowManager.LayoutParams.FILL_PARENT,
									   WindowManager.LayoutParams.FILL_PARENT);
	}

	private void doDebugSwapDialog() {

		ArrayList<String> msg = new ArrayList<String>();
		msg.addAll(Arrays.asList("Program", "Scalars", "Array", "List", "Stack", "Bundle", "Watch"));
		final String[] names = {
			"View Program", "View Scalars", "View Array", "View List",
			"View Stack",   "View Bundle",  "View Watch", "View Console"
		};

		LayoutInflater inflater = getLayoutInflater();
		View dialogLayout = inflater.inflate(R.layout.debug_list_s_layout, null);

		ListView debugView = (ListView)dialogLayout.findViewById(R.id.debug_list_s);
		debugView.setAdapter(new ArrayAdapter<String>(this, R.layout.simple_list_layout_1, msg));
		debugView.setVerticalScrollBarEnabled(true);
		debugView.setClickable(true);

		debugView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
				DialogSelector(position);
				boolean dosel = 
					(dbDialogArray  && WatchedArray  == -1) ||
					(dbDialogList   && WatchedList   == -1) ||
					(dbDialogStack  && WatchedStack  == -1) ||
					(dbDialogBundle && WatchedBundle == -1);
				if (dosel) {
					// if the element has not been defined ask if user wishes to do so.
					// or at least this is where it will go.
					// for now, default to view program.
					DialogSelector(0);
					position = 0;
				}
				String name = (position < names.length) ? names[position] : "";
				Toaster(name).show();
			}
		});

		AlertDialog.Builder builder = new AlertDialog.Builder(this)
			.setCancelable(true)
			.setTitle("Select View:")
			.setView(dialogLayout);

		builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
			public void onCancel(DialogInterface arg0) {
				WaitForSwap = false;
			}
		});

		builder.setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog,int which) {
				WaitForSwap = false;
				dbSwap = false;
			}
		});

		/*  // leave out until the element selector is done.
		builder.setNeutralButton("Choose Element", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog,int which){
				WaitForSelect = true;
			}
		});
		*/

		dbSwapDialog = builder.show();
		dbSwapDialog.getWindow().setLayout(WindowManager.LayoutParams.FILL_PARENT,
										   WindowManager.LayoutParams.FILL_PARENT);
	}

	private Toast Toaster(CharSequence msg) {			// default: short, high toast
		Toast toast = Toaster(msg, Toast.LENGTH_SHORT);
		toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 50);
		return toast;
	}

	private Toast Toaster(CharSequence msg, int duration) {
		Toast toast = Toast.makeText(this, msg, duration);
		return toast;
	}

	private void doDebugSelectDialog() {
		if (dbSelectDialog != null) { dbSelectDialog.dismiss(); }

		ArrayList<String> msg = new ArrayList<String>();
		// TODO: What did Michael have in mind?
	}

	private ArrayList<String> dbDoWatch(String prefix) {
		ArrayList<String> msg = new ArrayList<String>();
		msg.add("Watching:");

		int count = VarNames.size();
		if (!WatchVarIndex.isEmpty()) {
			int watchcount = WatchVarIndex.size();
			for (int j = 0; j < watchcount; ++j) {
				int wvi = WatchVarIndex.get(j);
				if (wvi < count) {
					String line = dbDoOneScalar(wvi, prefix);
					if (line != null) { msg.add(line); }
				} else {
					msg.add(Watch_VarNames.get(j) + " = Undefined");
				}
			}
		} else { msg.add("\n" + "Undefined."); }
		return msg;
	}

	private ArrayList<String> dbDoFunc() {
		ArrayList<String> msg = new ArrayList<String>();
		String msgs = "";
		if (!FunctionStack.isEmpty()) {
			Stack<Bundle> tempStack = (Stack<Bundle>) FunctionStack.clone();
			do {
				msgs = tempStack.pop().getString("fname") + msgs;
			} while (!tempStack.isEmpty());
		} else { msgs += "MainProgram"; }
		msg.add("In Function: " + msgs);
		return msg;
	}

	private ArrayList<String> dbDoScalars(String prefix) {
		ArrayList<String> msg = new ArrayList<String>();
		msg.add("Scalar Dump");
		int count = VarNames.size();
		for (int varNum = 0; varNum < count; ++varNum) {
			String line = dbDoOneScalar(varNum, prefix);
			if (line != null) { msg.add(line); }
		}
		return msg;
	}

	private String dbDoOneScalar(int varNum, String prefix) {
		String var = VarNames.get(varNum);
		int len = (var == null) ? 0 : var.length();
		if (len == 0) {
			return(prefix + "Warning: zero-length variable name");
		}
		char last = var.charAt(len - 1);
		boolean isScalar = (last != '(') && (last != '[');
		if (isScalar) {
			boolean isString = (last == '$');
			String line = prefix + var;
			Integer Index = VarIndex.get(varNum).intValue();
			if (Index == null) {
				line += ": Warning: null variable index";
			} else {
				int index = Index.intValue();
				line += " = "
					 + (isString ? quote(StringVarValues.get(index))
								 : NumericVarValues.get(index).toString());
			}
			return line;
		}
		return null;
	}

	private ArrayList<String> dbDoArray(String prefix) {
		ArrayList<String> msg = new ArrayList<String>();
		String var = VarNames.get(WatchedArray);
		msg.add("Dumping Array " + var + "]");

		Bundle ArrayEntry = ArrayTable.get(VarIndex.get(WatchedArray));	// Get the array table bundle for this array
		if (ArrayEntry == null) {
			msg.add(prefix + "Warning: null array table entry");
		} else {
			int length = ArrayEntry.getInt("length");			// get the array length
			int base = ArrayEntry.getInt("base");				// and the start of the array in the variable space
			// ArrayList<Integer> dims = ArrayEntry.getIntegerArrayList("dims");
			// ArrayList<Integer> sizes = ArrayEntry.getIntegerArrayList("sizes");
			// msg.add("dims: " + dims.toString());
			// msg.add("sizes: " + sizes.toString());
			boolean isString = var.endsWith("$[");
			for (int i = 0; i < length; ++i) {
				msg.add(prefix +
						(isString ? quote(StringVarValues.get(base + i))
								  : NumericVarValues.get(base + i).toString()));
			}
		}
		return msg;
	}

	private ArrayList<String> dbDoList(String prefix) {
		ArrayList<String> msg = new ArrayList<String>();
		msg.add("Dumping List " + WatchedList);

		if ((WatchedList < 0) || (WatchedList >= theLists.size())) {
			msg.add(prefix + "List has not been created.");
			return msg;
		}

		ArrayList list = theLists.get(WatchedList);				// get the list
		if (list == null) {
			msg.add(prefix + "Warning: null list variable");
			return msg;
		}

		int length = list.size();
		if (length == 0) {
			msg.add(prefix + "Empty List");
		} else {
			boolean isString = (theListsType.get(WatchedList) == list_is_string);
			for (Object item : list) {							// get each item
				String line;
				if (item == null) {
					line = "Warning: null list item";
				} else {
					line = item.toString();
					if (isString) { line = quote(line); }
				}
				msg.add(prefix + line);
			}
		}
		return msg;
	}

	private ArrayList<String> dbDoStack(String prefix) {
		ArrayList<String> msg = new ArrayList<String>();
		msg.add("Dumping stack " + WatchedStack);

		if ((WatchedStack < 0) || (WatchedStack >= theStacks.size())) {
			msg.add(prefix + "Stack has not been created.");
			return msg;
		}

		Stack stack = theStacks.get(WatchedStack);				// get the stack
		if (stack == null) {
			msg.add(prefix + "Warning: null list variable");
		} else if (stack.isEmpty()) {
			msg.add(prefix + "Empty Stack");
		} else {
			Stack tempStack = (Stack)stack.clone();
			boolean isString = (theStacksType.get(WatchedStack) == list_is_string);
			do {
				String line;
				Object item = tempStack.pop();					// get each item
				if (item == null) {
					line = "Warning: null stack item";
				} else {
					line = item.toString();
					if (isString) { line = quote(line); }
				}
				msg.add(prefix + line);
			} while (!tempStack.isEmpty());
		}
		return msg;
	}

	private ArrayList<String> dbDoBundle(String prefix) {
		ArrayList<String> msg = new ArrayList<String>();
		msg.add("Dumping Bundle " + WatchedBundle);

		if ((WatchedBundle < 0) || (WatchedBundle >= theBundles.size())) {
			msg.add(prefix + "Bundle has not been created.");
			return msg;
		}

		Bundle b = theBundles.get(WatchedBundle);				// get the bundle
		if (b == null) {
			msg.add(prefix + "Warning: null bundle variable");
			return msg;
		}

		Set<String> set = b.keySet();
		if (set.size() == 0) {
			msg.add(prefix + "Empty Bundle");
			return msg;
		}

		for (String s : set) {
			if (!s.startsWith("@@@N.")) {
				boolean isNumeric = b.getBoolean("@@@N." + s);
				msg.add(prefix + s + ": " +
						(isNumeric ? Double.toString(b.getDouble(s))
								   : quote(b.getString(s))));
			}
		}
		return msg;
	}

	public boolean handleDebugMessage(Message msg) {
		switch (msg.what) {
		case MESSAGE_DEBUG_DIALOG: doDebugDialog();       break;
		case MESSAGE_DEBUG_SWAP:   doDebugSwapDialog();   break;
		case MESSAGE_DEBUG_SELECT: doDebugSelectDialog(); break;
		default:
			return false;										// message not recognized
		}
		return true;											// message handled
	}

// ******************************* User Defined Functions ******************************

	private boolean  executeFN_DEF(){								// Define Function

		String var = getNewFNVar();									// Get the function name
		if (var == null) { return false; }
		int fVarNumber = createNewVar(var);							// Save the VarNumber of the function name
		boolean fType = VarIsNumeric;

		ArrayList<String> fVarName = new ArrayList<String>();		// List of parameter names
		ArrayList<Integer> fVarType = new ArrayList<Integer>();		// List of parameter types
		ArrayList<Integer> fVarArray = new ArrayList<Integer>();	// A list of indicating if parm is array

		if (!isNext(')')) {
			do {													// Get each of the parameter names
				String name = parseVar(!USER_FN_OK);				// without creating any new vars
				if (name == null) { return false; }
				if (VarIsArray) {									// Process array
					if (!isNext(']')) {								// Array must not have any indices
						return RunTimeError(EXPECT_ARRAY_NO_INDEX);
					}
					fVarArray.add(1);								// 1 Indicates var is array
				} else fVarArray.add(0);							// 0 Indicates var is not an array

				fVarName.add(name);									// Save the name
				fVarType.add(VarIsNumeric ? 1 : 0);					// Save the type
			} while (isNext(','));
			if ( !(isNext(')') && checkEOL()) ) { return false; }
		}

		Bundle b = new Bundle();									// Build the bundle for the FunctionTable
		b.putInt("line", ExecutingLineIndex);						// Line number of fn.def
		b.putString("fname", VarNames.get(fVarNumber));				// Name of this function
		b.putBoolean("isNumeric", fType);							// Type of function
		b.putStringArrayList("pnames", fVarName);					// List of parameter names
		b.putIntegerArrayList("ptype", fVarType);					// List of parameter types
		b.putIntegerArrayList("array", fVarArray);					// List of parameter array flags
		
		int fn = FunctionTable.size();
		FunctionTable.add(b);										// Add the Bundle to the function table

		VarIndex.set(fVarNumber, fn);								// Associate the function number with
																	// the function name
		int max = Basic.lines.size();
		while (++ExecutingLineIndex < max) {						// Now scan for the fn.end
			ExecutingLineBuffer = Basic.lines.get(ExecutingLineIndex);
			if (ExecutingLineBuffer.startsWith("fn.end")) {			// Break out when found
				return true;
			}
			if (ExecutingLineBuffer.startsWith("fn.def")) {			// Insure not trying to define function in function
				return RunTimeError("Can not define a function within a function at:");
			}
		}
		return RunTimeError("No fn.end for this function");			// end of program, fn.end not found
	}

	private boolean  executeFN_RTN(){
		if (FunctionStack.empty()){							// Insure RTN actually called from executing function
			RunTimeError("misplaced fn.rtn");
			return false;
		}
		
		Bundle fsb = new Bundle();							
		fsb = FunctionStack.peek();							// Look at the Function Bundle on the stack
		
		if (fsb.getBoolean("isNum")){						// to determine if function is string or numeric
			if (!evalNumericExpression()) return false;
		}else{
			if (!evalStringExpression()) return false;
		}
		
		fnRTN = true;										// Signal RunLoop() to return
		return checkEOL();
	}

	private boolean executeFN_END(){
		if (FunctionStack.empty()){							// Insure END actually called from executing function
			RunTimeError("misplaced fn.end");
			return false;
		}

		Bundle fsb = new Bundle();							
		fsb = FunctionStack.peek();							// Look at the Function Bundle on the stack

		if (fsb.getBoolean("isNum")){						// to determine if function is string or numeric
			EvalNumericExpressionValue = 0.0;				// Set default value
		}else{
			StringConstant = "";							// Set default value
		}

		fnRTN = true;										// Signal RunLoop() to return
		return checkEOL();
	}

	private boolean executeCALL(){
		if (isUserFunction(TYPE_NUMERIC)) return doUserFunction();
		if (isUserFunction(TYPE_STRING)) return doUserFunction();
		return false;
	}

	private boolean isUserFunction(boolean isNumeric){

		if (FunctionTable.size() == 0) return false;					// If function table empty, return fail

		for (Bundle b : FunctionTable) {								// for each Bundle in the Function Table
			String name = b.getString("fname");							// get the function name
			if (ExecutingLineBuffer.startsWith(name, LineIndex)) {		// if in list
				if (isNumeric != b.getBoolean("isNumeric")) return false;
				ufBundle = b;
				LineIndex += name.length();
				return true;											// report found
			}
		}
		return false;													// report fail
	}

	private boolean doUserFunction(){

	Bundle fsb = new Bundle();											// The Function Stack Bundle

	int sVarNames = VarNames.size();									// Save a bunch of things that will need to be restored
	int sVarIndex = VarIndex.size();
	fsb.putBoolean("isNum", ufBundle.getBoolean("isNumeric"));
	fsb.putInt("SVN", sVarNames);
	fsb.putInt("VI", sVarIndex);
	fsb.putInt("VSS", VarSearchStart);
	fsb.putInt("NVV", NumericVarValues.size());
	fsb.putInt("SVV", StringVarValues.size());
	fsb.putInt("AT", ArrayTable.size());
	fsb.putInt("ELI", ExecutingLineIndex);
	fsb.putString("PKW", PossibleKeyWord);
	fsb.putString("fname",ufBundle.getString("fname"));

	ArrayList<String> fVarName;											// The list of Parm Var Names
	ArrayList<Integer> fVarType;										// and the parm types
	ArrayList<Integer> fVarArray;										// and the parm types
	
	fVarName = ufBundle.getStringArrayList("pnames"); 					// Get the Names and Types from the ufBundle                  
	fVarType = ufBundle.getIntegerArrayList("ptype");					// ufBundle set by isUserFunction()
	fVarArray = ufBundle.getIntegerArrayList("array");

	int pCount = fVarName.size();										// The number of parameter

	int i = 0;
	if (pCount != 0) {													// For each parameter
		do {
			if (i >= pCount) {											// Insure no more parms than defined
				return RunTimeError("Calling parameter count exceeds defined parameter count");
			}

			boolean isGlobal = isNext('&');								// optional for scalars, ignored for arrays
			boolean typeIsNumeric = (fVarType.get(i) != 0);
			if (fVarArray.get(i) == 1){									// if this parm is an array
				if (getArrayVarForRead() == null) { return false; }		// get the array name var
				if (!isNext(']')) {										// must be no indices
					return RunTimeError(EXPECT_ARRAY_NO_INDEX);
				} else  if (typeIsNumeric != VarIsNumeric) {			// insure type (string or number) match
					return RunTimeError("Array parameter type mismatch at:");
				}

				VarNames.add(fVarName.get(i));							// and add the var name
				VarIndex.add(VarIndex.get(VarNumber));					// copy array table pointer to the new array.
			} // end array
			else {
				if (isGlobal) {
					String var = getVarAndType();						// if this is a Global Var
					if (var == null)		{ return false; }			// then must be var not expression
					if (VarIsNew) { return RunTimeError("Call by reference vars must be predefined"); }
					if (typeIsNumeric != VarIsNumeric) {				// insure type (string or number) match
						return RunTimeError("Global parameter type mismatch at:");
					}
					if (!getVarValue(var))	{ return false; }			// bottom half of getVar()

					VarNames.add(fVarName.get(i));						// add the called var name to the var name table
					VarIndex.add(theValueIndex);						// but give it the value index of the calling var
				} // end global
				else {
					if (!typeIsNumeric) {								// if parm is string
						if (!evalStringExpression()) {					// get the string value
							return RunTimeError("Parameter type mismatch at:");
						}else{
							VarIndex.add(StringVarValues.size());				// Put the string value into the
							StringVarValues.add(StringConstant);				// string var values table
							VarNames.add(fVarName.get(i));						// and add the var name
						}
					} else {
						if (!evalNumericExpression()) {					// if parm is number get the numeric value
							return RunTimeError("Parameter type mismatch at:");
						} else {
							VarIndex.add(NumericVarValues.size());				// Put the number values into the
							NumericVarValues.add(EvalNumericExpressionValue);	// numeric var values table
							VarNames.add(fVarName.get(i));						// and add the var name
						}
					}
				}
			} // end scalar

			++i;											//  Keep going while calling parms exist

		} while ( isNext(','));
	} // end if

	if (i != pCount) { return RunTimeError("Too few calling parameters at:"); }

	// Every function must have a closing right parenthesis.
	if (!isNext(')')) { return false; }

	fsb.putInt("LI", LineIndex);                        // Save out index into the line buffer

	FunctionStack.push(fsb);							// Push the function bundle
	VarSearchStart = sVarNames;							// Set the new start location for var name searches

	ExecutingLineIndex = ufBundle.getInt("line")+1;     // Set to execute first line after fn.def statement

	fnRTN = false;										// Will be set true by fn.rtn
														// cause RunLoop() to return when true

	boolean flag = theBackground.RunLoop();				// Now go execute the function

	if (FunctionStack.isEmpty()){
		RunTimeError("System problem. Wait 10 seconds before rerunning.");
		return false;
	}

	fsb = FunctionStack.pop();							// Function execution done. Restore stuff

	trimArray(VarNames, fsb.getInt("SVN"));
	trimArray(VarIndex, fsb.getInt("VI"));
	VarSearchStart = fsb.getInt("VSS");
	trimArray(NumericVarValues, fsb.getInt("NVV"));
	trimArray(StringVarValues, fsb.getInt("SVV"));
	trimArray(ArrayTable, fsb.getInt("AT"));
	ExecutingLineIndex = fsb.getInt("ELI");
	LineIndex = fsb.getInt("LI");
	PossibleKeyWord = fsb.getString("PKW");

	ExecutingLineBuffer = Basic.lines.get(ExecutingLineIndex);

	return flag;                                      // Pass on the pass/fail state from the function
	}

// **************************** SWITCH Statements *************************************

	private boolean executeSW_BEGIN(){
		boolean isNumeric;
		double nexp = 0;
		String sexp = "";
		if (evalNumericExpression()){
			isNumeric = true;
			nexp = EvalNumericExpressionValue;
		}else if (evalStringExpression()){
			isNumeric = false;
			sexp = StringConstant;
		}else return false;
		if (!checkEOL()) { return false; }

		while (++ExecutingLineIndex < Basic.lines.size()) {
			ExecutingLineBuffer = Basic.lines.get(ExecutingLineIndex);  // Next program line
			if (ExecutingLineBuffer.startsWith("sw.end")) {
				LineIndex = 6;
				return (checkEOL());
			}
			if (ExecutingLineBuffer.startsWith("sw.default")){
				LineIndex = 10;
				return (checkEOL());
			}
			if (ExecutingLineBuffer.startsWith("sw.case")){
				LineIndex = 7;
				if (isNumeric){
					if (!evalNumericExpression()) return false;
					if (!checkEOL()) return false;
					if (nexp == EvalNumericExpressionValue) return true;
				}else{
					if (!evalStringExpression()) return false;
					if (!checkEOL()) return false;
					if (sexp.equals(StringConstant)) return true;
				}
			}
		}
		RunTimeError("No sw.end after sw.begin");
		return false;
	}
	
	private boolean executeSW_CASE(){
		return true;
	}
	
	private boolean executeSW_BREAK(){
		if (!checkEOL()) { return false; }
		while (++ExecutingLineIndex < Basic.lines.size()){
			ExecutingLineBuffer = Basic.lines.get(ExecutingLineIndex);  // Next program line
			if (ExecutingLineBuffer.startsWith("sw.end")) {
				LineIndex = 6;
				return checkEOL();
			}
		}
		RunTimeError("sw.xxxx without sw.end");
		return false;
	}
	
	private boolean executeSW_DEFAULT(){
		return true;
	}
	
	private boolean executeSW_END(){
		return true;
	}
	
// *****************************  End of core Basic Methods ****************************
	
// ***************************** Data I/O Operations ***********************************

	// args: stream to flush, previous exception or null;
	// return: previous exception if any, else new exception if any, else null 
	private static IOException flushStream(Flushable stream, IOException ex) {	// flush a stream
		if (stream == null) { return ex; }
		try { stream.flush(); return ex; }
		catch ( IOException e ) { return (ex == null) ? e : ex; }
	}

	// args: stream to close, previous exception or null
	// return: previous exception if any, else new exception if any, else null 
	private static IOException closeStream(Closeable stream, IOException ex) {	// close a stream
		if (stream == null) { return ex; }
		try { stream.close(); return ex; }
		catch ( IOException e ) { return (ex == null) ? e : ex; }
	}

	private boolean checkReadFile(int FileNumber, ArrayList<?> list){		// Validate input file for read commands
		// Note: if list is null, list size is not checked - needed for POSITION_GET
		if (FileTable.size() == 0)                  { RunTimeError("No files opened"); }
		else if (FileNumber < 0)                    { RunTimeError("Read file did not exist"); }
		else if (list != null && list.size() == 0)  { RunTimeError("No files opened for read"); }
		else if (FileNumber >= FileTable.size())    { RunTimeError("Invalid File Number at"); }
		return !SyntaxError;				// SyntaxError is true if RunTimeError was called
	}

	private boolean checkWriteFile(int FileNumber, ArrayList<?> list){		// Validate input file for write commands 
		if (FileTable.size() == 0)                  { RunTimeError("No files opened"); }
		else if (list.size() == 0)                  { RunTimeError("No files opened for write"); }
		else if (FileNumber >= FileTable.size() || FileNumber < 0)
													{ RunTimeError("Invalid File Number at"); }
		return !SyntaxError;				// SyntaxError is true if RunTimeError was called
	}

	private boolean checkReadTextAttributes(Bundle FileEntry) {				// Validate common FileEntry items
																			// for commands that read text files
		int FileMode = FileEntry.getInt("mode");
		boolean closed = FileEntry.getBoolean("closed");
		boolean isText = FileEntry.getBoolean("isText");

		if (!isText)              { RunTimeError("File not opened for text"); }
		else if (closed)          { RunTimeError("File is closed"); }
		else if (FileMode != FMR) { RunTimeError("File not opened for read at"); }
		return !SyntaxError;				// SyntaxError is true if RunTimeError was called
	}

	private boolean checkReadByteAttributes(Bundle FileEntry) {				// Validate common FileEntry items
																			// for commands that read binary files
		int FileMode = FileEntry.getInt("mode");
		boolean closed = FileEntry.getBoolean("closed");
		boolean isText = FileEntry.getBoolean("isText");

		if (isText)               { RunTimeError("File not opened for byte"); }
		else if (closed)          { RunTimeError("File is closed"); }
		else if (FileMode != FMR) { RunTimeError("File not opened for read at"); }
		return !SyntaxError;				// SyntaxError is true if RunTimeError was called
	}

	private boolean checkWriteTextAttributes(Bundle FileEntry) {			// Validate common FileEntry items
																			// for commands that write text files
		int FileMode = FileEntry.getInt("mode");
		boolean closed = FileEntry.getBoolean("closed");
		boolean isText = FileEntry.getBoolean("isText");

		if (!isText)              { RunTimeError("File not opened for text"); }
		else if (closed)          { RunTimeError("File is closed"); }
		else if (FileMode != FMW) { RunTimeError("File not opened for write at"); }
		return !SyntaxError;				// SyntaxError is true if RunTimeError was called
	}

	private boolean checkWriteByteAttributes(Bundle FileEntry) {			// Validate common FileEntry items
																			// for commands that write binary files
		int FileMode = FileEntry.getInt("mode");
		boolean closed = FileEntry.getBoolean("closed");
		boolean isText = FileEntry.getBoolean("isText");

		if (isText)               { RunTimeError("File not opened for byte"); }
		else if (closed)          { RunTimeError("File is closed"); }
		else if (FileMode != FMW) { RunTimeError("File not opened for write at"); }
		return !SyntaxError;				// SyntaxError is true if RunTimeError was called
	}

	private boolean executeTEXT_OPEN(){										// Open a file
		boolean append = false;												// Assume not append
		int FileMode = 0;													// Default to FMR
		switch (ExecutingLineBuffer.charAt(LineIndex)) {					// First parm is a, w or r
		case 'a':
			append = true;					// append is a special case of write
		case 'w':							// write
			FileMode = FMW;
			++LineIndex;
			break;
		case 'r':							// read
			FileMode = FMR;
			++LineIndex;
		}
		if (!isNext(',')) { return false; }
		if (!getNVar()) { return false; }			// Next parameter is the FileNumber variable
		NumericVarValues.set(theValueIndex, (double) FileTable.size());
		int saveValueIndex = theValueIndex;			// Save in case read file not found.

		if (!isNext(',')) { return false; }
		if (!getStringArg()) { return false; }		// Final parameter is the filename
		String fileName = StringConstant;
		if (!checkEOL()) return false;

		Bundle FileEntry = new Bundle();			// Prepare the filetable bundle
		FileEntry.putInt("mode", FileMode);
		FileEntry.putBoolean("eof", false);
		FileEntry.putBoolean("closed",false);
		FileEntry.putInt("position", 1);
		FileEntry.putBoolean("isText", true);

		if (FileMode == FMR) {											// Read was selected
			BufferedReader buf = null;
			try { buf = Basic.getBufferedReader(Basic.DATA_DIR, fileName); } // closes stream if error on mark()
			catch (FileNotFoundException ex1) { }
			catch (NotFoundException ex2) { }
			catch (Exception e) { return RunTimeError("Problem: " + e + " at:"); } // Something other than not found (?)
			if (buf == null) {
				if (Basic.isAPK) {
					return false;
				} else {												// standard BASIC!
					NumericVarValues.set(saveValueIndex, (double) -1);	// change file index to report file does not exist
					return true;
				}
			}

			FileEntry.putInt("stream", BRlist.size());		// The stream parm indexes
			BRlist.add(buf);								// into the BRlist
			FileTable.add(FileEntry);
		}

		else if (FileMode == FMW) {										// Write Selected
			File file = new File(Basic.getDataPath(fileName));
			if (append && file.exists()) {
				FileEntry.putInt("position", (int) file.length()+1);
			} else {										// if not appending overwrite existing file
				try {										// if no file create a new one
					file.createNewFile();
				} catch (Exception e) {
					return RunTimeError(e);
				}
			}
			if (!(file.exists() && file.canWrite())) {
				RunTimeError("Problem opening " + fileName);
				return false;
			}

			FileWriter writer = null;
			try {
				writer = new FileWriter(file, append);		// open the filewriter for the SD Card
			} catch (Exception e) {
				return RunTimeError(e);
			}

			FileEntry.putInt("stream", FWlist.size());		// The stream parm indexes
			FWlist.add(writer);								// into the FWlist
			FileTable.add(FileEntry);
		}
		return true;													// Done
	}
	
	private  boolean executeTEXT_CLOSE(){
		if (FileTable.size() == 0){
			return true;
		}

		if (!evalNumericExpression()) { return false; }						// First parm is the filenumber expression
		int FileNumber = EvalNumericExpressionValue.intValue();
		if (FileNumber >= FileTable.size() || FileNumber < 0 ) {
			RunTimeError("Invalid File Number at");
			return false;
		}
		if (!checkEOL()) return false;

		Bundle FileEntry = FileTable.get(FileNumber);						// Get the bundle
		int FileMode = FileEntry.getInt("mode");
		boolean closed = FileEntry.getBoolean("closed");
		boolean isText = FileEntry.getBoolean("isText");

		if (!isText){
			RunTimeError("File not opened for text");
			return false;
		}
		
		if (closed) { return true; }							// Already closed
		
		if (FileMode == FMR) {									// Close file open for read
			BufferedReader buf = BRlist.get(FileEntry.getInt("stream"));
    		try {
    			buf.close();
    		} catch (Exception e) {
    			return RunTimeError(e);
			}
		}
		else if (FileMode == FMW) {								// close file open for write
			FileWriter writer = FWlist.get(FileEntry.getInt("stream"));
			try {
				writer.flush();
				writer.close();
			} catch (Exception e) {
    			return RunTimeError(e);
			}
		} else {
			RunTimeError("File not opened for read or write");
			return false;
		}
		FileEntry.putBoolean("closed", true);					// mark the file closed.
		return true;
	}
	
	private  boolean executeTEXT_READLN(){
		if (!evalNumericExpression()) { return false; }						// First parm is the filenumber expression
		int FileNumber = EvalNumericExpressionValue.intValue();
		if (!checkReadFile(FileNumber, BRlist)) { return false; }			// Check runtime errors

		if (!isNext(',')) { return false; }
		if (!getSVar()) { return false; }									// Second parm is the string var
		if (!checkEOL()) { return false; }

		Bundle FileEntry = FileTable.get(FileNumber);						// Get the bundle
		if (!checkReadTextAttributes(FileEntry)) { return false; }			// Check runtime errors

		boolean eof = FileEntry.getBoolean("eof");
		String data = null;
		if (eof) {											// If already eof don't read
			data = "EOF";									// but force returned data to "EOF"
		} else {
			BufferedReader buf = BRlist.get(FileEntry.getInt("stream"));
			try {											// Read a line
				data = buf.readLine();
			} catch (IOException e) {
				RunTimeError("I/O error at:");
				return false;
			}
			if (data == null){
				data = "EOF";								// Hit eof, force returned data
				FileEntry.putBoolean("eof", true);			// and mark Bundle
			} else {
				int p = FileEntry.getInt("position");		// Not eof, update position in Bundle
				FileEntry.putInt("position", ++p);
			}
		}
		StringVarValues.set(theValueIndex, data);			// Give the data to the user
		return true;
	}
	
	private boolean executeTEXT_WRITELN(){
		if (!evalNumericExpression()) { return false; }						// First parm is the filenumber expression
		int FileNumber = EvalNumericExpressionValue.intValue();

		if (!isNext(',')) { return false; }				// Set up to parse the stuff to print

		if (!buildPrintLine(textPrintLine, "\r\n")) return false;	// build up the text line in StringConstant
		if (!PrintLineReady) {							// flag set by buildPrintLine
			textPrintLine = StringConstant;				// not ready to print; hold line
			return true;								// and wait for next Text.Writeln command
		}
		textPrintLine = "";								// clear the accumulated text print line

		if (!checkWriteFile(FileNumber, FWlist)) { return false; }			// Check runtime errors
		Bundle FileEntry = FileTable.get(FileNumber);						// Get the bundle
		if (!checkWriteTextAttributes(FileEntry)) { return false; }			// Check runtime errors

		FileWriter writer = FWlist.get(FileEntry.getInt("stream"));
		try {
			writer.write(StringConstant);				// Oh, and write the line
		} catch (IOException e) {
			RunTimeError("I/O error at");
			return false;
		}
		
		int p = FileEntry.getInt("position");
		FileEntry.putInt("position", ++p);

		return true;
	}

	private boolean executeTEXT_INPUT(){
		if (!getSVar()) return false;
		int saveValueIndex = theValueIndex;

		TextInputString = "";
		String title = null;
		if (isNext(',')) {													// Check for optional parameter(s)
			boolean isComma = isNext(',');									// Look for second comma, two commas together
																			// mean initial text is skipped, use empty string
			if (!isComma) {
				if (!getStringArg()) return false;							// One comma so far; get initial input text
				TextInputString = StringConstant;
				isComma = isNext(',');										// Look again for second comma
			}
			if (isComma) {
				if (!getStringArg()) return false;							// Second comma; get title
				title = StringConstant;
			}
		}
		if (!checkEOL()) return false;

		Intent intent = new Intent(this, TextInput.class);
		if (title != null) { intent.putExtra("title", title); }
		HaveTextInput = false;
		startActivityForResult(intent, BASIC_GENERAL_INTENT);

		synchronized (LOCK) {
			while (!HaveTextInput) {
				try { LOCK.wait(); }										// Wait for signal from TextInput.java thread
				catch (InterruptedException e) { HaveTextInput = true; }
			}
		}
		StringVarValues.set(saveValueIndex, TextInputString);
		return true;
	}
		
	private boolean executeTEXT_POSITION_SET(){
		if (!evalNumericExpression()) { return false; }						// First parm is the filenumber expression
		int FileNumber = EvalNumericExpressionValue.intValue();
		if (!checkReadFile(FileNumber, BRlist)) { return false; }			// Check runtime errors

		if (!isNext(',')) { return false; }									// Second parm is the position var expression
		if (!evalNumericExpression()) {return false; }
		int pto = EvalNumericExpressionValue.intValue();
		if (pto < 1) {
			RunTimeError("Set position must be >= 1");
			return false;
		}
		if (!checkEOL()) { return false; }

		Bundle FileEntry = FileTable.get(FileNumber);						// Get the bundle
		if (!checkReadTextAttributes(FileEntry)) { return false; }			// Check runtime errors
		
		BufferedReader buf = BRlist.get(FileEntry.getInt("stream"));
		int pnow = FileEntry.getInt("position");
		boolean eof = FileEntry.getBoolean("eof");
		
		if (pto < pnow) {
			try {
				buf.reset();								// Back to start of file
			} catch (Exception e) {
				return RunTimeError(e);
			}
			eof = false;
			pnow = 1;
		}
		String data = null;
		while ((pnow < pto) && !eof) {
			try {											// Read a line
				data = buf.readLine();
			} catch (Exception e) {
				return RunTimeError(e);
			}
			if (data == null) {
				eof = true;									// Hit eof, mark Bundle
			} else {
				++pnow;										// Not eof, update position for Bundle
			}
		}
		FileEntry.putInt("position", pnow);					// Update Bundle
		FileEntry.putBoolean("eof", eof);
		return true;
	}

	private boolean executeTEXT_POSITION_GET(){
		if (!evalNumericExpression()) { return false; }						// First parm is the filenumber expression
		int FileNumber = EvalNumericExpressionValue.intValue();
		if (!checkReadFile(FileNumber, null)) { return false; }				// Check runtime errors

		if (!isNext(',')) { return false; }									// Second parm is the position var
		if (!getNVar()) { return false; }
		if (!checkEOL()) { return false; }

		Bundle FileEntry = FileTable.get(FileNumber);		// Get the bundle
		boolean isText = FileEntry.getBoolean("isText");
		if (!isText){
			RunTimeError("File not opened for text");
			return false;
		}

		double p = FileEntry.getInt("position");
		NumericVarValues.set(theValueIndex, p);

		return true;
	}
		
	private boolean executeTGET(){
		if (!getSVar()) return false;
		int saveValueIndex = theValueIndex;

		if (!isNext(',')) return false;
		if (!getStringArg()) return false;
		TextInputString = StringConstant;
		String Prompt = StringConstant;

		String title = null;
		if (isNext(',')) {
			if (!getStringArg()) return false;
			title = StringConstant;
		}
		if (!checkEOL()) return false;

		Intent intent = new Intent(this, TGet.class);
		if (title != null) { intent.putExtra("title", title); }
		HaveTextInput = false;
		startActivityForResult(intent, BASIC_GENERAL_INTENT);

		synchronized (LOCK) {
			while (!HaveTextInput) {
				try { LOCK.wait(); }										// Wait for signal from TGet.java thread
				catch (InterruptedException e) { HaveTextInput = true; }
			}
		}
		PrintShow(Prompt + TextInputString);

		StringVarValues.set(saveValueIndex, TextInputString);
		return true;
	}

	private boolean executeBYTE_OPEN(){										// Open a file
		boolean append = false;												// Assume not append
		int FileMode = 0;													// Default to FMR
		switch (ExecutingLineBuffer.charAt(LineIndex)) {					// First parm is a, w or r
		case 'a':
			append = true;					// append is a special case of write
		case 'w':							// write
			FileMode = FMW;
			++LineIndex;
			break;
		case 'r':							// read
			FileMode = FMR;
			++LineIndex;
		}
		if (!isNext(',')) { return false; }
		if (!getNVar()) { return false; }			// Next parameter is the FileNumber variable
		NumericVarValues.set(theValueIndex, (double) FileTable.size());
		int saveValueIndex = theValueIndex;			// Save in case read file not found.

		if (!isNext(',')) { return false; }
		if (!getStringArg()) { return false; }		// Final parameter is the filename
		String fileName = StringConstant;
		if (!checkEOL()) return false;

		Bundle FileEntry = new Bundle();			// Prepare the filetable bundle
		FileEntry.putInt("mode", FileMode);
		FileEntry.putBoolean("eof", false);
		FileEntry.putBoolean("closed",false);
		FileEntry.putInt("position", 1);
		FileEntry.putBoolean("isText", false);
		
		if (FileMode == FMR) {												// Read was selected
			BufferedInputStream buf = null;
			if (fileName.startsWith("http")) {
				try {
					URL url = new URL(fileName);
					URLConnection connection = url.openConnection();
					buf = new BufferedInputStream(connection.getInputStream());
				} catch (Exception e) {
					return RunTimeError("Problem: " + e + " at:");
				}
			} else {
				try { buf = Basic.getBufferedInputStream(Basic.DATA_DIR, fileName); }
				catch (FileNotFoundException ex1) { }
				catch (NotFoundException ex2) { }
				catch (Exception e) { return RunTimeError("Problem: " + e + " at:"); } // Something other than not found (?)
				if (buf == null) {
					if (Basic.isAPK) {
						return false;
					} else {												// standard BASIC!
						NumericVarValues.set(saveValueIndex, (double) -1);	// change file index report file does not exist
						return true;
					}
				}
			}

			FileEntry.putInt("stream", BISlist.size());		// The stream parm indexes
			BISlist.add(buf);								// into the BISlist
			FileTable.add(FileEntry);
		}

		else if (FileMode == FMW) {											// Write Selected
																			// Write to SD Card
			File file = new File(Basic.getDataPath(fileName));
			if (append && file.exists()) {
				FileEntry.putInt("position", (int) file.length()+1);
			} else {										// if not appending overwrite existing file
				try {										// if no file create a new one
					file.createNewFile();
				} catch (Exception e) {
					return RunTimeError(e);
				}
			}
			if (!(file.exists() && file.canWrite())) {
				RunTimeError("Problem opening " + fileName);
				return false;
			}

			String afile = file.getAbsolutePath();
			DataOutputStream dos = null;
			try {
				FileOutputStream fos = new FileOutputStream(afile, append);
				dos = new DataOutputStream(fos);
			}				
			catch (Exception e) {
				return RunTimeError(e);
			}
			
			FileEntry.putInt("stream", DOSlist.size());		// The stream parm indexes
			DOSlist.add(dos);								// into the DOSlist
			FileTable.add(FileEntry);
		}
		return true;														// Done
	}
	
	private  boolean executeBYTE_CLOSE(){
		if (FileTable.size() == 0){
			return true;
		}

		if (!evalNumericExpression()) { return false; }						// First parm is the filenumber expression
		int FileNumber = EvalNumericExpressionValue.intValue();
		if (FileNumber >= FileTable.size() || FileNumber < 0) {
			RunTimeError("Invalid File Number at");
			return false;
		}
		if (!checkEOL()) return false;

		Bundle FileEntry = FileTable.get(FileNumber);		// Get the bundle
		int FileMode = FileEntry.getInt("mode");
		boolean closed = FileEntry.getBoolean("closed");
		boolean isText = FileEntry.getBoolean("isText");

		if (isText){
			RunTimeError("File not opened for byte");
			return false;
		}

		if (closed) { return true; }						// Already closed

		if (FileMode == FMR) {								// Close file open for read
			BufferedInputStream bis = BISlist.get(FileEntry.getInt("stream"));
			try {
				bis.close();
			} catch (IOException e) {
//				Log.e(LOGTAG, e.getLocalizedMessage() + " 3");
				return RunTimeError(e);
			}
		} else if (FileMode == FMW) {						// close file open for write
			DataOutputStream dos = DOSlist.get(FileEntry.getInt("stream"));
			try {
				dos.flush();
				dos.close();
			} catch (IOException e) {
//				Log.e(LOGTAG, e.getLocalizedMessage() + " 3");
				return RunTimeError(e);
			}
		} else {
			RunTimeError("File not opened for read or write");
			return false;
		}
		FileEntry.putBoolean("closed", true);				// mark the file closed.
		return true;
	}
	
	private boolean executeBYTE_COPY(){
		if (!evalNumericExpression()) { return false; }						// First parm is the source filenumber expression
		int FileNumber = EvalNumericExpressionValue.intValue();
		if (!checkReadFile(FileNumber, BISlist)) { return false; }			// Check runtime errors

		if (!isNext(',')) { return false; }
		if (!evalStringExpression()) { return false; }						// Second parm is the destination file name
		if (!checkEOL()) { return false; }

		Bundle FileEntry = FileTable.get(FileNumber);						// Get the bundle
		if (!checkReadByteAttributes(FileEntry)) { return false; }			// Check runtime errors

		boolean eof = FileEntry.getBoolean("eof");
		if (eof){											// Check not EOF
			RunTimeError("Attempt to read beyond the EOF at:");
			return false;
		}
		
		BufferedInputStream bis = BISlist.get(FileEntry.getInt("stream"));
		int p = FileEntry.getInt("position");
		
		String theFileName = StringConstant;
		File file = new File(Basic.getDataPath(theFileName));

		try {
			file.createNewFile();
		} catch (IOException e) {
			RunTimeError(theFileName + " I/O Error");
			return false;
		}
		if (!file.exists() || !file.canWrite()) {
			RunTimeError("Problem opening " + theFileName);
			return false;
		}
		
		BufferedOutputStream bos = null;
		try {
			bos = new BufferedOutputStream(new FileOutputStream(file), 8192);
			byte[] buffer = new byte[1024];
			int len = 0;

			while((len = bis.read(buffer)) != -1) {
				bos.write(buffer, 0, len);
				p += len;
			}
		} catch (Exception e) {
			RunTimeError("Exception: " + e);
			return false;
		} finally {
			try { bos.flush(); } catch (Exception e) { return RunTimeError(e); }
			try { bis.close(); } catch (Exception e) { return RunTimeError(e); }
			try { bos.close(); } catch (Exception e) { return RunTimeError(e); }
		}
		FileEntry.putInt("position", p);
		FileEntry.putBoolean("eof", true);
		return true;
	}

	private  boolean executeBYTE_READ_BYTE(){
		if (!evalNumericExpression()) { return false; }						// First parm is the filenumber expression
		int FileNumber = EvalNumericExpressionValue.intValue();
		if (!checkReadFile(FileNumber, BISlist)) { return false; }			// Check runtime errors
		
		if (!isNext(',')) { return false; }
		if (!getNVar()) { return false; }									// Second parm is the return data var
		if (!checkEOL()) { return false; }
		
		Bundle FileEntry = FileTable.get(FileNumber);						// Get the bundle
		if (!checkReadByteAttributes(FileEntry)) { return false; }			// Check runtime errors

		boolean eof = FileEntry.getBoolean("eof");
		int data = -1;
		if (!eof) {											// If already eof don't read
			BufferedInputStream bis = BISlist.get(FileEntry.getInt("stream"));
			int p = FileEntry.getInt("position");
			try {
				data = bis.read();							// Read a byte
			} catch (Exception e) {
				return RunTimeError(e);
			}
			if (data < 0) {
				FileEntry.putBoolean("eof", true);			// Hit eof, mark Bundle
			} else {
				FileEntry.putInt("position", ++p);			// Not eof, update position in Bundle
			}
		}
		NumericVarValues.set(theValueIndex, (double) data);	// Give the data to the user
		return true;
	}
		
	private boolean executeBYTE_READ_BUFFER(){
		if (!evalNumericExpression()) { return false; }						// First parm is the filenumber expression
		int FileNumber = EvalNumericExpressionValue.intValue();
		if (!checkReadFile(FileNumber, BISlist)) { return false; }			// Check runtime errors

		if (!isNext(',')) { return false; }
		if (!evalNumericExpression()) { return false; }						// Second parm is the byte count
		int byteCount = EvalNumericExpressionValue.intValue();

		if (!isNext(',')) { return false; }									// Third parm is the return buffer string
		if (!getSVar()) { return false; }
		if (!checkEOL()) { return false; }
		
		Bundle FileEntry = FileTable.get(FileNumber);						// Get the bundle
		if (!checkReadByteAttributes(FileEntry)) { return false; }			// Check runtime errors

		boolean eof = FileEntry.getBoolean("eof");
		String buff = "";
		if (!eof) {											// If already eof don't read
			BufferedInputStream bis = BISlist.get(FileEntry.getInt("stream"));
			int p = FileEntry.getInt("position");
			byte[] byteArray = new byte[byteCount];
			int count = 0;
			try {
				count = bis.read(byteArray, 0, byteCount);	// Read the bytes
			}catch (Exception e) {
				return RunTimeError(e);
			}
			if (count < 0) {
				FileEntry.putBoolean("eof", true);			// Hit eof, mark Bundle
			} else {
				FileEntry.putInt("position", p + count);	// No eof, update position in Bundle
				buff = new String(byteArray, 0);			// convert bytes to String for user
				if (count < byteCount) {
					buff = buff.substring(0, count);
				}
			}
		}
		StringVarValues.set(theValueIndex, buff);			// Give the data to the user
		return true;
	}
	
	private  boolean executeBYTE_WRITE_BYTE(){
		if (!evalNumericExpression()) { return false; }						// First parm is the filenumber expression
		int FileNumber = EvalNumericExpressionValue.intValue();
		if (!checkWriteFile(FileNumber, DOSlist)) { return false; }			// Check runtime errors

		if (!isNext(',')) { return false; }									// Second parm is the byte var

		byte b = 0;
		boolean OutputIsByte = true;
		if (evalNumericExpression()) {
			b = EvalNumericExpressionValue.byteValue();
		} else {
			if (!evalStringExpression()) { return false; }
			OutputIsByte = false;
		}
		if (!checkEOL()) { return false; }
	
		Bundle FileEntry = FileTable.get(FileNumber);						// Get the bundle
		if (!checkWriteByteAttributes(FileEntry)) { return false; }			// Check runtime errors

		DataOutputStream dos = DOSlist.get(FileEntry.getInt("stream"));
		int p = FileEntry.getInt("position");
		try {
			if (OutputIsByte) {
				dos.writeByte(b);							// Oh, and write the byte
				++p;
			} else {
				int len = StringConstant.length();
				for (int k = 0; k < len; ++k) {				// or bytes
					b = (byte)StringConstant.charAt(k);
					dos.writeByte(b);
				}
				p += len;
			}
		} catch (IOException e) {
			RunTimeError("I/O error at");
			return false;
		}
		FileEntry.putInt("position", p);					// update position in Bundle
		return true;
	}
		
	private boolean executeBYTE_WRITE_BUFFER(){
		if (!evalNumericExpression()) { return false; }						// First parm is the filenumber expression
		int FileNumber = EvalNumericExpressionValue.intValue();
		if (!checkWriteFile(FileNumber, DOSlist)) { return false; }			// Check runtime errors

		if (!isNext(',')) { return false; }									// Second parm is the buffer
		if (!evalStringExpression()) { return false; }
		if (!checkEOL()) { return false; }

		Bundle FileEntry = FileTable.get(FileNumber);						// Get the bundle
		if (!checkWriteByteAttributes(FileEntry)) { return false; }			// Check runtime errors

		DataOutputStream dos = DOSlist.get(FileEntry.getInt("stream"));
		int p = FileEntry.getInt("position");
		int len = StringConstant.length();
		try {
			for (int k = 0; k < len; ++k) {									// Write the buffer
				byte bb = (byte)StringConstant.charAt(k);
				dos.writeByte(bb);
			}
		} catch (IOException e) {
			RunTimeError("I/O error at");
			return false;
		}
		p += len;
		FileEntry.putInt("position", p);					// update position in Bundle
		return true;
		}

		
	private boolean executeBYTE_POSITION_SET(){
		if (!evalNumericExpression()) { return false; }						// First parm is the filenumber expression
		int FileNumber = EvalNumericExpressionValue.intValue();
		if (!checkReadFile(FileNumber, BISlist)) { return false; }			// Check runtime errors

		if (!isNext(',')) { return false; }									// Second parm is the position var expression
		if (!evalNumericExpression()) { return false; }
		int pto = EvalNumericExpressionValue.intValue();
		if (pto < 1) {
			RunTimeError("Set position must be >= 1");
			return false;
		}
		if (!checkEOL()) { return false; }

		Bundle FileEntry = FileTable.get(FileNumber);						// Get the bundle
		if (!checkReadByteAttributes(FileEntry)) { return false; }			// Check runtime errors
		
		BufferedInputStream bis = BISlist.get(FileEntry.getInt("stream"));
		int pnow = FileEntry.getInt("position");
		boolean eof = FileEntry.getBoolean("eof");

		if (pto < pnow) {
			try {
				bis.reset();								// Back to start of file
			} catch (Exception e) {
				return RunTimeError(e);
			}
			eof = false;
			pnow = 1;
		}
		if ((pnow != pto) && !eof) {
			int skip = pto - pnow - 1;						// Skip plus single read will get to target position
			int skipped = 0;
			int data = -1;
			do {
				try {
					int avail = bis.available();			// Don't skip past eof
					skipped += (int)bis.skip(Math.min(skip - skipped, avail));
					data = bis.read();						// Read to check eof
				} catch (Exception e) {
					return RunTimeError(e);
				}
				if (data >= 0) { ++skipped; }				// If byte was read, count it
				else { eof = true; break; }					// otherwise mark eof in Bundle
			} while (skipped < skip);
			pnow += skipped;								// Count bytes skipped
		}
		FileEntry.putInt("position", pnow);					// Update Bundle
		FileEntry.putBoolean("eof", eof);
		return true;
	}
		
	private boolean executeBYTE_POSITION_GET(){
		if (!evalNumericExpression()) { return false; }						// First parm is the filenumber expression
		int FileNumber = EvalNumericExpressionValue.intValue();
		if (!checkReadFile(FileNumber, null)) { return false; }				// Check runtime errors

		if (!isNext(',')) { return false; }									// Second parm is the position var
		if (!getNVar()){return false;}
		if (!checkEOL()) { return false; }

		Bundle FileEntry = FileTable.get(FileNumber);		// Get the bundle
		boolean isText = FileEntry.getBoolean("isText");
		if (isText){
			RunTimeError("File not opened for byte");
			return false;
		}

		double p = FileEntry.getInt("position");
		NumericVarValues.set(theValueIndex, p);

		return true;
	}

	// ***************************** File Operations ***********************************

	private boolean checkSDCARD(char mount) {				// mount is 'w' for writable,
															// 'r' for either readable or writable 
		boolean okay = Basic.checkSDCARD(mount);
		if (!okay) { RunTimeError("SDCARD not available."); }
		return okay;
	}

	private boolean executeMKDIR(){
		if (!getStringArg()) { return false; }				// get the path
		String path = StringConstant;
		if (!checkEOL()) { return false; }

		File file = new File(Basic.getDataPath(path));
		file.mkdirs();
		if (!file.exists()){								// did we get a dir?
			return RunTimeError(path + " mkdir failed");
		}
		
		return true;
	}

	private boolean executeRENAME(){
		if (!getStringArg()) { return false; }				// get the old file name
		String Old = StringConstant;

		if (!isNext(',')) { return false; }
		if (!getStringArg()) { return false; }				// get the new file name
		String New = StringConstant;

		if (!checkEOL()) { return false; }
		if (!checkSDCARD('w')) { return false; }

		File oldFile = new File(Basic.getDataPath(Old));
		if (!oldFile.exists()) {							// does the file exist?
			return RunTimeError(Old + " directory/file not in this path");
		}
		File newFile = new File(Basic.getDataPath(New));

		if (!oldFile.renameTo(newFile)) {					// try to rename it
			return RunTimeError("Rename of " + Old + " to " + New + " failed");
		}
		return true;
	}

	private boolean executeDELETE(){

		if (!getNVar()) { return false; }					// get the var to put the result into
		int SaveValueIndex = theValueIndex;

		if (!isNext(',')) { return false; }
		if (!getStringArg()) { return false; }				// get the file name
		String fileName = StringConstant;

		if (!checkEOL()) { return false; }
		if (!checkSDCARD('w')) { return false; }

		File file = new File(Basic.getDataPath(fileName));
		double result = file.delete() ? 1 : 0;				// try to delete it
		NumericVarValues.set(SaveValueIndex, result);
		return true;
	}

	private boolean executeFILE() {							// Get File command keyword if it is there
		return executeCommand(file_cmd, "File");
	}

	private boolean executeFILE_EXISTS(){
		if (!getNVar()) { return false; }					// get the var to put the result into
		int saveValueIndex = theValueIndex;

		if (!isNext(',')) { return false; }
		if (!getStringArg()) { return false; }				// get the file name
		String fileName = StringConstant;

		if (!checkEOL()) { return false; }
		if (!checkSDCARD('r')) { return false; }

		double exists = 0.0;								// "false"
		if (!fileName.equals("")) {							// empty file name would report parent dir exists; catch it and report false
			File file = new File(Basic.getDataPath(fileName));
			if (file.exists()) exists = 1.0;				// if file exists, report "true"
		}
		NumericVarValues.set(saveValueIndex, exists);
		return true;
	}

	private boolean isResourceFile(int resID) {
		boolean isFile = false;
		if (resID != 0) {
			InputStream inputStream= null;
			try {
				inputStream = getResources().openRawResource(resID);
				isFile = true;
			}
			catch (Exception ex) { }						// eat exception and return false
			finally { closeStream(inputStream, null); }
		}
		return isFile;
	}

	private String getAssetType(String assetPath) {			// return type "d" or "f", or null if not found
		AssetManager am = getAssets();
		try {
			String[] list = am.list(assetPath);
			if (list.length != 0) { return "d"; }			// it's a directory (no empty directories in assets)
		} catch (IOException e) { }

		try {												// only works for some file extensions
			AssetFileDescriptor afd = am.openFd(assetPath);
			try { afd.close(); } catch (IOException e) { }	// clean up
			return "f";										// it's a file
		} catch (IOException e) { Log.d(LOGTAG, "getAssetType:openFD:" + e); }

		try {												// last ditch, should always work
			InputStream is = am.open(assetPath);
			try { is.close(); } catch (IOException e) { }	// clean up
			return "f";										// it's a file
		} catch (IOException e) { Log.d(LOGTAG, "getAssetType:open:" + e); }

		return null;										// asset not found
	}

	private long getResourceSize(String fileName) {
		long size = -1;
		int resID = Basic.getRawResourceID(fileName);
		if (resID != 0) {
			InputStream inputStream= null;
			try {
				inputStream = getResources().openRawResource(resID);
				size = inputStream.available();
			}
			catch (Exception ex) { }						// eat exception and return -1
			finally { closeStream(inputStream, null); }
		}
		return size;
	}

	private long getAssetSize(String fileName) {			// get the size of a file in assets
		String assetPath = Basic.getAppFilePath(Basic.DATA_DIR, fileName);
		AssetManager am = getAssets();

		try {
			String[] list = am.list(assetPath);
			if (list.length != 0) { return 0; }				// it's a directory (no empty directories in assets)
		} catch (IOException e) { }

		long size = AssetFileDescriptor.UNKNOWN_LENGTH;
		try {												// try the easy way:
			AssetFileDescriptor afd = am.openFd(assetPath);	// get afd
			size = afd.getLength();							// and ask it for the length
			try { afd.close(); } catch (IOException e) { }	// clean up
			if (size != AssetFileDescriptor.UNKNOWN_LENGTH) { return size; }
		} catch (IOException e) { Log.d(LOGTAG, "getAssetSize:openFD:" + e); }

		BufferedInputStream bis = null;
		try {												// no afd or length unknown
			InputStream is = am.open(assetPath);			// open the file
			size = is.available();							// and check available
			if (size != AssetFileDescriptor.UNKNOWN_LENGTH) { return size; }

			// Opened file but length is unknown.
			// Last ditch attempt: read the file and count its bytes
			byte[] bytes = new byte[8192];
			long bytesRead = 0;
			bis = new BufferedInputStream(is);
			for (int count = 0; count != -1; count = bis.read(bytes, 0, 8192)) {
				bytesRead += count;
			}
			size = bytesRead;
		} catch (IOException e) { Log.d(LOGTAG, "getAssetSize:open:" + e); }
		finally { closeStream(bis, null); }

		return ((size == AssetFileDescriptor.UNKNOWN_LENGTH) ? -1 : size);
	}

	private boolean executeFILE_SIZE(){
		if (!getNVar()) { return false; }					// get the var to put the size value into
		int SaveValueIndex = theValueIndex;
		long size = -1;

		if (!isNext(',')) { return false; }
		if (!getStringArg()) { return false; }				// get the file name
		String fileName = StringConstant;

		if (!checkEOL()) { return false; }
		if (!checkSDCARD('r')) { return false; }

		File file = new File(Basic.getDataPath(fileName));
		if (file.exists()) {
			size = file.length();							// the file exists
		} else {											// the file does not exist
			if (Basic.isAPK) {								// we are in APK
				size = getResourceSize(fileName);			// try to get it from raw resource
				if (size == -1) {							// resource not found
					size = getAssetSize(fileName);			// try to get it from assets
				}
			}
		}
		if (size == -1) {									// not file, resource, or asset
			return RunTimeError(fileName + " not found");
		}
		NumericVarValues.set(SaveValueIndex, (double)size);	// Put the file size into the var

		return true;
	}


	private boolean executeFILE_TYPE() {
		if (!getSVar()) { return false; }					// get the var to put the type info into
		int SaveValueIndex = theValueIndex;

		if (!isNext(',')) { return false; }
		if (!getStringArg()) { return false; }				// get the file name
		String fileName = StringConstant;

		if (!checkEOL()) { return false; }
		if (!checkSDCARD('r')) { return false; }

		String type = "x";										// assume does not exist
		File file = new File(Basic.getDataPath(fileName));
		if (file.exists()) {
			type = file.isDirectory() ? "d" :
				   file.isFile()      ? "f" : "o";
		} else {												// does not exist in file system
			if (Basic.isAPK) {									// we are in APK
				int resID = Basic.getRawResourceID(fileName);
				if (resID != 0) {
					type = isResourceFile(resID) ? "f" : "o";	// file or other, can't be a directory
				} else {
					String assetPath = Basic.getAppFilePath(Basic.DATA_DIR, fileName);
					String aType = getAssetType(assetPath);		// file or directory, can't be other
					if (aType != null) { type = aType; }		// else "x", does not exist
				}
			}													// else "x", does not exist
		}
		StringVarValues.set(SaveValueIndex, type);			// put the file type into the var

		return true;
	}

	private boolean executeFILE_ROOTS(){
		if (!getSVar()) { return false; }
		int SaveVarIndex = theValueIndex;

		if (!checkEOL()) { return false; }
		if (!checkSDCARD('r')) { return false; }

		String path = Basic.getDataPath(null);		// get canonical path to default data directory
		StringVarValues.set(SaveVarIndex, path);
		return true;
	}

	private boolean executeDIR(){
		if (!getStringArg()) { return false; }						// get the path
		String filePath = StringConstant;

		if (!isNext(',')) { return false; }							// parse the ,D$[]
		String var = getArrayVarForWrite(TYPE_STRING);				// get the result array variable
		if (var == null) { return false; }							// must name a new string array variable
		String dirMark = "(d)";
		if (isNext(',')) {											// optional directory marker
			if (!getStringArg()) { return false; }
			dirMark = StringConstant;
		}
		if (!checkEOL()) { return false; }							// line must end with ']'

		File lbDir = new File(Basic.getDataPath(filePath));
		if (!lbDir.exists()) {										// error if directory does not exist
			return RunTimeError(filePath + " is invalid path");
		}

		ArrayList <String> files = new ArrayList<String>();
		ArrayList <String> dirs = new ArrayList<String>();

		String FL[] = lbDir.list();									// get the list of files in the dir
		if (FL == null) {											// if not a dir
			dirs.add(" ");											// make list with one element
		} else {
											// Go through the file list and mark directory entries with dirMark
			String absPath = lbDir.getAbsolutePath() + '/';
			for (String s : FL) {
				File test = new File(absPath + s);
				if (test.isDirectory()) {							// If file is a directory
					dirs.add(s + dirMark);							// mark it and add it to display list
				} else {
					files.add(s);									// else add name without the directory mark
				}
			}
			Collections.sort(dirs);									// Sort the directory list
			Collections.sort(files);								// Sort the file list
			dirs.addAll(files);										// copy the file list to end of dir list
		}
		int length = dirs.size();									// number of directories and files in list
		if (length == 0) { length = 1; }							// make at least one element if dir is empty
																	// it will be an empty string
		return ListToBasicStringArray(var, dirs, length);			// Copy the list to a BASIC! array
	}

	private boolean executeGRABFILE(){
		if (!getSVar()) { return false; }							// First parm is string var
		int saveVarIndex = theValueIndex;

		if (!isNext(',')) { return false; }
		if (!getStringArg()) { return false; }						// Second parm is the filename
		String theFileName = StringConstant;

		boolean textFlag = false;									// Default: assume ASCII/binary
		if (isNext(',')) {											// Optional third parm
			if (!evalNumericExpression()) { return false; }
			textFlag = (EvalNumericExpressionValue != 0.0);			// is the text flag: unicode if non-zero
		}
		if (!checkEOL()) { return false; }
		if (!checkSDCARD('r')) { return false; }

		BufferedInputStream bis = null;
		String result = "";
		IOException ioex = null;
		Exception ex = null;

		try {
			bis = Basic.getBufferedInputStream(Basic.DATA_DIR, theFileName);
			result = grabStream(bis, textFlag);
		}
		catch (IOException ie) { ioex = ie; }
		catch (Exception e) { ex = e; }
		finally {
			ioex = closeStream(bis, ioex);
			if (ioex != null) { return RunTimeError(ioex); }		// Report first exception, if any, and if no previous RTE set
			if (ex != null) { return RunTimeError(ex); }
		}
		StringVarValues.set(saveVarIndex, result);
		return true;
	}

	private boolean executeGRABURL(){
		if (!getSVar()) { return false; }							// First parm is string var
		int saveVarIndex = theValueIndex;

		if (!isNext(',')) { return false; }
		if (!getStringArg()) { return false; }						// Second parm is the url

		int timeoutMillis = 0;										// Default: assume infinite timeout
		if (isNext(',')) {											// Optional third parm
			if (!evalNumericExpression()) { return false; }
			timeoutMillis = EvalNumericExpressionValue.intValue();	// is the timeout: infinite if 0
			if (timeoutMillis < 0) { timeoutMillis *= -1; }			// negative value would throw an exception
		}
		if (!checkEOL()) { return false; }

		BufferedInputStream bis = null;
		String result = null;

		URL url = null;
		try {
			// This assumes that you have a URL from which the response will come
			url = new URL(StringConstant);
			// Open a connection to the URL and obtain a buffered input stream
			URLConnection connection = url.openConnection();
			if (timeoutMillis != 0) {
				connection.setConnectTimeout(timeoutMillis);
				connection.setReadTimeout(timeoutMillis);
			}
			InputStream inputStream = connection.getInputStream();
			bis = new BufferedInputStream(inputStream);
			result = grabStream(bis, true);							// Read as encoded text stream, not byte stream
// Alternate implementation: uncomment this catch block to handle Timeout explicitly.
//		} catch (SocketTimeoutException ste) {						// Connect or Read timeout
//			result = "";											// Empty result, finally will close stream but will not return
		} catch (Exception e) {										// Report exception in run time error
			return RunTimeError(e);									// finally will close stream before return happens
		} finally {
			IOException ex = closeStream(bis, null);				// Close stream if not already closed
			if (ex != null) { return RunTimeError(ex); }			// Report first exception, if any, and if no previous RTE set
		}
		StringVarValues.set(saveVarIndex, result);
		return true;
	}

	private String grabStream(BufferedInputStream bis, boolean textFlag) throws IOException {
		ByteArrayBuffer byteArray = new ByteArrayBuffer(1024*8);
		int current = 0;
		// Read from the stream into a byte array
		while ((current = bis.read()) != -1) {
			byteArray.append((byte) current);
		}

		// Construct a String object from the byte array containing the response
		if (textFlag) {
			return new String(byteArray.toByteArray());			// Text: keep full two-byte encoding
		} else {
			return new String(byteArray.toByteArray(), 0);		// ASCII or binary: force upper byte 0
		}
	}

	// ************************************** Time and TimeZone commands **************************
	
	private boolean executeTIME(){								// Get the date and time
		Time time = theTimeZone.equals("") ? new Time() : new Time(theTimeZone); // If user has set a time zone, use it
		if (evalNumericExpression()) {							// If there is a numeric argument it is a time in ms
			if (!isNext(',')) { return checkEOL(); }			// Done if no other arguments
			time.set(EvalNumericExpressionValue.longValue());	// Use the time argument
		} else {
			time.setToNow();									// No arg, or first arg is not numeric: time is now
		}
		String theTime[] = time.format("%Y:%m:%d:%H:%M:%S").split(":");
		int i = 0;
		do {													// String vars for time components
																// Commas hold places for up to six svars.
			if (getSVar()) { StringVarValues.set(theValueIndex, theTime[i]); } // If svar, use it; if nothing, skip to next comma.
		} while ((++i < 6) && isNext(','));						// Anything else will get caught by checkEOL
		if (isNext(',') && getNVar()) {							// Another comma holds a place for an optional nvar
			double weekDay = time.weekDay + 1;					// for day of week: 1 is Sunday
			NumericVarValues.set(theValueIndex, weekDay);
		}
		if (isNext(',') && getNVar()) {							// Another comma holds a place for an optional nvar
																// For Daylight Saving Time flag
			NumericVarValues.set(theValueIndex, Math.signum((double) time.isDst)); // 1 yes, 0 no, -1 unknown
		}
		return checkEOL();
	}

	private boolean executeTIMEZONE(){										// Get TimeZone command keyword if it is there
		return executeCommand(TimeZone_cmd, "TimeZone");
	}

	private boolean executeTIMEZONE_SET() {									// Set a global Time Zone string for TIME and TIME(
		String zone = Time.getCurrentTimezone();							// default to local time zone
		if (getStringArg()) {
			TimeZone tz = TimeZone.getTimeZone(StringConstant);				// if arg, use it as TimeZone ID
			zone = tz.getID();												// read back ID, "GMT" if user-string invalid
		}
		if (!checkEOL()) { return false; }
		theTimeZone = zone;
		return true;
	}

	private boolean executeTIMEZONE_GET() {									// Get the time zone setting
		if (!(getSVar() && checkEOL())) { return false; }
		String zone = theTimeZone;
		if (zone.equals("")) {
			zone = Time.getCurrentTimezone();								// If user never set a time zone, use local
		}
		StringVarValues.set(theValueIndex, zone);
		return true;
	}

	private boolean executeTIMEZONE_LIST() {								// Get a list of all valid time zone strings
		if (!(getNVar() && checkEOL())) { return false; }

		int theIndex = createNewList(list_is_string);						// Create a new empty string list
		if (theIndex < 0) { return false; }									// Create failed
		NumericVarValues.set(theValueIndex, (double) theIndex);				// Return the list pointer

		ArrayList<String> theList = theLists.get(theIndex);
		for (String zone : TimeZone.getAvailableIDs()) {					// Get all the zones the system knows 
			theList.add(zone);												// Put them in the list
		}
		return true;
	}

	// ************************************** Miscellaneous Non-core commands **************************

	private boolean executePAUSE(){
		if (!evalNumericExpression()) return false;							// Get pause duration value
		if (!checkEOL()) return false;
		long dur = EvalNumericExpressionValue.longValue();
		if (dur < 1) {
			return RunTimeError("Pause must be greater than zero");
		}

		try { Thread.sleep(dur); } catch (InterruptedException e) {}
		return true;
	}

	  private boolean executeBROWSE(){
		  
		  if (!evalStringExpression()){return false;}
		  if (SEisLE) return false;
		  if (!checkEOL()) return false;

		  String url = StringConstant;
		  Intent i = new Intent(Intent.ACTION_VIEW);						  // Go to the html page
//		  Intent i = new Intent(Intent.CATEGORY_BROWSABLE);
		  i.setData(Uri.parse(url));
      i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      	try {Thread.sleep(500);}catch(InterruptedException e){}              // Sleep here stopped forced stop exceptions

		  try {startActivity(i);}
//		  catch ( android.content.ActivityNotFoundException  e){
		  catch ( Exception  e){
			  return RunTimeError(e);
		  }
		  return true;
	  }

	  private boolean executeINKEY(){
		  
		  if (!getSVar()) return false;						// get the var to put the key value into
		  if (!checkEOL()) return false;
		  if (InChar.size() > 0){
			  StringVarValues.set(theValueIndex, InChar.get(0));
			  InChar.remove(0);
		  } else
			  StringVarValues.set(theValueIndex, "@");
		  return true;
	  }
	  
	  private boolean executeONKEY_RESUME(){
		  if (interruptResume == -1 ) {
			  RunTimeError("No Current Key Interrupt");
			  return false;
		  }
		  return doResume();
	  }

	private boolean executePOPUP(){
		if (!getStringArg()) return false;						// get the message
		ToastMsg = StringConstant;

		if (!isNext(',')) return false;
		if (!evalNumericExpression()) return false;				// get x
		ToastX = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;
		if (!evalNumericExpression()) return false;				// get y
		ToastY = EvalNumericExpressionValue.intValue();

		if (!isNext(',')) return false;
		if (!evalNumericExpression()) return false;				// get duration
		ToastDuration = (EvalNumericExpressionValue == 0.0)
					  ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG;
		if (!checkEOL()) return false;

		PrintShow("@@4");										// tell the UI Task to pop the toast
		return true;
	}

	public boolean executeCLS(){							// Clear Screen
		if (!checkEOL()) return false;
		PrintShow("@@5");										// signal UI task
		return true;
	}

	public boolean executeSELECT(){

		if (!getNVar()) return false;								// get the var to put the key value into
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		ArrayList<String> selectList;
		int saveLineIndex = LineIndex;
		if ((getVarAndType() != null) && VarIsArray) {
			if (!isNext(']'))	{ return RunTimeError(EXPECT_ARRAY_NO_INDEX); } // Array must not have any indices
			if (VarIsNumeric)	{ return RunTimeError(EXPECT_STRING_ARRAY); }
			if (VarIsNew)		{ return RunTimeError(EXPECT_DIM_ARRAY); }

			Bundle ArrayEntry = ArrayTable.get(VarIndex.get(VarNumber)); // Get the array table bundle for this array
			int length = ArrayEntry.getInt("length");				// get the array length
			int base = ArrayEntry.getInt("base");					// and the start of values in the value space

			selectList = new ArrayList<String>();					// Create a list to copy array values into

			for (int i = 0; i < length; ++i) { 						// Copy the array values into that list
				selectList.add(StringVarValues.get(base+i));
			}
		} else {
			LineIndex = saveLineIndex;
			if (!evalNumericExpression()) return false;

			int listIndex = EvalNumericExpressionValue.intValue();
			if (listIndex < 1 || listIndex >= theLists.size()){
				return RunTimeError("Invalid List Pointer");
			}
			if (theListsType.get(listIndex) != list_is_string){
				return RunTimeError("Not a string list");
			}
			selectList = theLists.get(listIndex);
		}

		String title = null;										// set defaults
		String msg = null;
		int isLongClickValueIndex = -1;

		if (isNext(',')) {											// comma indicates optional arguments
			boolean isComma = true;
			if (!isNext(',') && !isEOL() && getStringArg()) {
				title = msg = StringConstant;						// user provided a title argument
				isComma = isNext(',');
			}
			if (isComma) {
				if (isNext(',')) {
					msg = "";										// user suppressed message
				} else if (isEOL()) {
					isComma = false;
				} else if (getStringArg()) {
					msg = StringConstant;							// user provided a message argument
					isComma = isNext(',');
				}
			}
			if (isComma) {
				if (!getNVar()) return false;						// get the long press var
				isLongClickValueIndex = theValueIndex;
			}
		}
		if (!checkEOL()) return false;

		SelectedItem = 0;											// intialize return values
		ItemSelected = false;
		SelectLongClick = false;

		Intent intent = new Intent(this, Select.class);
		if (title != null) { intent.putExtra(Select.EXTRA_TITLE, title); }
		if (msg != null)   { intent.putExtra(Select.EXTRA_MSG, msg); }
		intent.putStringArrayListExtra(Select.EXTRA_LIST, selectList);
		startActivityForResult(intent, BASIC_GENERAL_INTENT);

		synchronized (LOCK) {
			while (!ItemSelected) {
				try { LOCK.wait(); }								// Wait for signal from Selected.java thread
				catch (InterruptedException e) { ItemSelected = true; }
			}
		}
		NumericVarValues.set(SaveValueIndex, (double) SelectedItem); // Put the item selected into the var

		if (isLongClickValueIndex != -1) {
			double isLongPress = SelectLongClick ? 1 : 0;			// Get the actual value
			NumericVarValues.set(isLongClickValueIndex, isLongPress); // Set the return value
		}

		return true;
	}

	private boolean executeSPLIT(int limit){

		String var = getArrayVarForWrite(TYPE_STRING);				// get the result array variable
		if (var == null) { return false; }							// must name a new string array variable

		if (!isNext(',')) { return false; }
		if (!getStringArg()) { return false; }						// Get the string to split
		String SearchString = StringConstant;

		String r[] = doSplit(SearchString, limit);					// Get regex arg, if any, and split the string.
		if (!checkEOL()) { return false; }

		int length = r.length;										// Get the number of strings generated
		if (length == 0) { return false; }							// error in doSplit()

		return ListToBasicStringArray(var, Arrays.asList(r), length);
	}

	private String[] doSplit(String SearchString, int limit) {		// Split a string
		// If limit < 0, keep all fields. If limit is 0, trim trailing blank fields.
		// If limit > 0, keep only up to limit fields.
		String r[] = new String[0];									// If error, return zero-length string
		String REString = null;
		if (isNext(',')) {											// If user command supplied a regex
			if (!getStringArg()) { return r; }						// get it
			REString = StringConstant;
		} else {
			REString = "\\s+";										// Otherwise split on whitespace
		}
		try {
			r = SearchString.split(REString, limit);
			if (r.length == 0) {									// Special case: REString same as SearchString
				r = new String[1];									// Return non-empty array
				r[0] = "";											// with one empty String
			}
		} catch (PatternSyntaxException pse) {
			RunTimeError(REString + " is invalid argument at");
		} catch (Exception e) {
			RunTimeError(e);
		}
		return r;
	}

	  private boolean executeKBSHOW(){
		  if (!checkEOL()) return false;
			Log.v(LOGTAG, CLASSTAG + " KBSHOW  " + kbShown );

		  if (kbShown) return true;
		  
		  if (GRFront) {
//			  GR.GraphicsImm.toggleSoftInputFromWindow(GR.drawView.getWindowToken(), InputMethodManager.SHOW_FORCED, 0);
//			  GR.GraphicsImm.showSoftInput(GR.drawView, InputMethodManager.SHOW_FORCED);
		      GR.GraphicsImm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
		      kbShown = true;
		  }
		  else {
//			  IMM.toggleSoftInputFromWindow(lv.getWindowToken(), InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_NOT_ALWAYS);
//			  IMM.showSoftInput(lv, 0);
			  IMM.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
			  kbShown = true;
		  }
//		  IMM.showSoftInputFromInputMethod(lv.getWindowToken(), InputMethodManager.SHOW_FORCED);
//		  IMM.showSoftInputFromInputMethod (lv.getWindowToken(), IMM.SHOW_FORCED);
		  return true;
	  }
	  
	  
	  private boolean executeKBHIDE(){
			if (!checkEOL()) return false;
			Log.v(LOGTAG, CLASSTAG + " KBHIDE " + kbShown);

		  if (GRFront) {
//			  GR.GraphicsImm.toggleSoftInputFromWindow(GR.drawView.getWindowToken(), InputMethodManager.SHOW_FORCED, 0);
			  GR.GraphicsImm.hideSoftInputFromWindow(GR.drawView.getWindowToken(), 0);
		      kbShown = false;
		      }
		  else {
//			  IMM.toggleSoftInputFromWindow(lv.getWindowToken(), InputMethodManager.SHOW_FORCED, 0);
			  IMM.hideSoftInputFromWindow(lv.getWindowToken(), 0);
			  kbShown = false;
		  }
		  return true;
	  }

	private boolean executeWAKELOCK(){
		if (!evalNumericExpression()) return false;					// Get setting
		int code  = EvalNumericExpressionValue.intValue();
		if (!checkEOL()) return false;

		if (theWakeLock != null) {
			theWakeLock.release();
			theWakeLock = null;
		}

		PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
		String tag = "BASIC!";
		switch (code) {
			case partial:
				theWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, tag);
				theWakeLock.acquire();
				break;
			case dim:
				theWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, tag);
				theWakeLock.acquire();
				break;
			case bright:
				theWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, tag);
				theWakeLock.acquire();
				break;
			case full:
				theWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, tag);
				theWakeLock.acquire();
				break;
			case release:
				break;
			default:
				return RunTimeError("WakeLock code not 1 - 5");
		}

		return true;
	}

	@SuppressLint("InlinedApi")										// Uses a value from API 12
	private boolean executeWIFILOCK(){
		if (!evalNumericExpression()) return false;					// Get setting
		int code  = EvalNumericExpressionValue.intValue();
		if (!checkEOL()) return false;

		if (theWifiLock != null) {
			theWifiLock.release();
			theWifiLock = null;
		}

		WifiManager wm = (WifiManager)getSystemService(Context.WIFI_SERVICE);
		String tag = "BASIC!";
		switch (code) {
			case wifi_mode_high:
				if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {	// >= 12
					theWifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, tag);
					theWifiLock.acquire();
					break;
				}							// Lower API versions fall through to MODE_FULL
			case wifi_mode_full:
				theWifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL, tag);
				theWifiLock.acquire();
				break;
			case wifi_mode_scan:
				theWifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, tag);
				theWifiLock.acquire();
				break;
			case wifi_release:
				break;
			default:
				return RunTimeError("WifiLock code not 1 - 4");
		}

		return true;
	}

	  private boolean executeTONE(){
		  
		    double duration = 1; 				// seconds
		    double freqOfTone = 1000; 			// hz
		    int sampleRate = 8000;				// a number

		  if (!evalNumericExpression()) return false;			// Get frequency
		  freqOfTone =  EvalNumericExpressionValue;
		  
		  if (!isNext(',')) return false;

		  if (!evalNumericExpression()) return false;			// Get duration
		  duration= EvalNumericExpressionValue/1000 ;
	
	    	double dnumSamples = duration * sampleRate;
	    	dnumSamples = Math.ceil(dnumSamples);
	    	int numSamples = (int) dnumSamples;
	    	double sample[] = new double[numSamples];
	    	ByteBuffer generatedSnd = ByteBuffer.allocate(2 * numSamples);
	    	generatedSnd.order(ByteOrder.LITTLE_ENDIAN);
	    	ShortBuffer shortView = generatedSnd.asShortBuffer();
	    	
	    	boolean flagMinBuff = true;							// Optionally skip checking min buffer size
	    	if (isNext(',')) {
	    		if (!evalNumericExpression()) return false;
	    		if (EvalNumericExpressionValue == 0 )
	    			flagMinBuff = false;
	    	}
	    	
	    	if (flagMinBuff) {
	    	
	    		int minBuffer = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_MONO,
	    				AudioFormat.ENCODING_PCM_16BIT);
	    		if (2 * numSamples< minBuffer){
	    			double minDuration = Math.ceil(1000 * (double)minBuffer/(2 * (double)sampleRate));
	    			RunTimeError("Minimum tone duration for this device: " + (int) minDuration + " milliseconds");
	    			return false;
	    		}
	    	}

	    	if (!checkEOL()) return false;				// No more parameters expected
	    	
	        for (int i = 0; i < numSamples; ++i) {		// Fill the sample array
	        	sample[i] = Math.sin(freqOfTone * 2 * Math.PI * i / (sampleRate));
	        }

	        // convert to 16 bit pcm sound array
	        // assumes the sample buffer is normalised.
	        int i = 0 ;
	        
	        int ramp = numSamples / 20 ;									// Amplitude ramp as a percent of sample count
	       
	        
	        for (i = 0; i< ramp; ++i) {										// Ramp amplitude up to max (to avoid clicks)
	            short val = (short) (sample[i] * 32767 * i/ramp);
	            shortView.put(val);
	        }

	        
	        for ( ; i< numSamples - ramp; ++i) {							// Max amplitude for most of the samples
	            short val = (short) (sample[i] * 32767);					// scale to maximum amplitude
	            shortView.put(val);
	        }
	        
	        for ( ; i< numSamples; ++i) {									// Ramp amplitude down to 0
	            short val = (short) (sample[i] * 32767 * (numSamples-i)/ramp);
	            shortView.put(val);
	        }
	        
	        AudioTrack audioTrack = null;									// Get audio track
	        try {
	        	audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
	        			sampleRate, AudioFormat.CHANNEL_OUT_MONO,
	        			AudioFormat.ENCODING_PCM_16BIT, numSamples*2,
	        			AudioTrack.MODE_STATIC);
	        	audioTrack.write(generatedSnd.array(), 0, numSamples*2);			// Load the track
	        	audioTrack.play();											// Play the track
	        }
	        catch (Exception e){
	        	return RunTimeError(e);
	        }
	        
	        int x =0;
	        do{														// Montior playback to find when done
	        	 if (audioTrack != null) 
	        		 x = audioTrack.getPlaybackHeadPosition(); 
	        	 else 
	        		 x = numSamples;	        
	        }while (x<numSamples);
	        
	        if (audioTrack != null) audioTrack.release();			// Track play done. Release track.
	        
	        audioTrack = null;										// Release storage
	        shortView = null;
	        generatedSnd = null;
	        sample = null;
	        System.gc();
	        

	        return true;
	  }

	private boolean executeVIBRATE(){

		if (getArrayVarForRead() == null) return false;				// Get the array variable
		if (!VarIsNumeric) { return RunTimeError(EXPECT_NUM_ARRAY); } // Insure that it is a numeric array
		int arrayTableIndex = VarIndex.get(VarNumber);

		Integer[] p = { null, null };
		if (!getIndexPair(p)) return false;							// Get values inside [], if any

		if (!isNext(',')) return false;								// Get the repeat value
		if(!evalNumericExpression()) return false;
		int repeat = EvalNumericExpressionValue.intValue();
		if (!checkEOL()) return false;

		if (!getArraySegment(arrayTableIndex, p)) { return false; }	// Get array base and length
		int base = p[0].intValue();
		int length = p[1].intValue();

		long Pattern[] = new long[length];							// Pattern array
		for (int i = 0; i < length; ++i) {							// Copy user array into pattern
			Pattern[i] = NumericVarValues.get(base + i).longValue();
		}

		if (repeat > 0) myVib.cancel();
		else myVib.vibrate(Pattern, repeat);						// Do the vibrate

		return true;
	}

	private boolean executeDEVICE(){
		  
		  if (!getSVar()) return false;
		  
		  String info = "";
		  info = info + "Brand = " + Build.BRAND + "\n";
		  info = info + "Model = " + Build.MODEL + "\n";
		  info = info + "Device = " + Build.DEVICE + "\n";
		  info = info + "Product = " + Build.PRODUCT + "\n";
		  info = info + "OS = " + Build.VERSION.RELEASE;
		  
		  StringVarValues.set(theValueIndex, info);
		  
		  return true;
	}

	private boolean executeHTTP_POST(){
		if (!getStringArg()) return false;
		String url = StringConstant;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;
		int theListIndex = EvalNumericExpressionValue.intValue();
		if (theListIndex < 1 || theListIndex>= theLists.size()) {
			return RunTimeError("Invalid list pointer");
		}
		if (theListsType.get(theListIndex) == list_is_numeric) {
			return RunTimeError("List must be of string type.");
		}
	
		List<String> thisList = theLists.get(theListIndex);
		int r = thisList.size() % 2;
		if (r !=0 ) {
			return RunTimeError("List must have even number of elements");
		}
	
	       List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
	       for (int i = 0; i <thisList.size(); ++i){
	    	   nameValuePairs.add(new BasicNameValuePair(thisList.get(i), thisList.get(++i)));
	       }
	       

		   String Result = "";
		   HttpClient client = new DefaultHttpClient();
		   HttpPost post = new HttpPost(url);
		   try {
			   post.setEntity(new UrlEncodedFormEntity(nameValuePairs, HTTP.UTF_8));
//			   HttpResponse response = client.execute(post);

		        ResponseHandler<String> responseHandler=new BasicResponseHandler();
		         Result = client.execute(post, responseHandler);

			} catch (Exception e) {
				return RunTimeError("! " + e);
			}

		if (!isNext(',')) return false;
		if (!getSVar()) return false;
		if (!checkEOL()) return false;
		StringVarValues.set(theValueIndex, Result);

		return true;
	}

	// ************************************************ SQL Package ***************************************

	private boolean executeSQL(){									// Get SQL command keyword if it is there
		return executeCommand(SQL_cmd, "SQL");
	}

	private boolean getDbPtrArg() {									// first arg of command is DB Pointer Variable
		String errStr = "Database not opened at:";
		if (DataBases.isEmpty()) {
			RunTimeError(errStr);
			return false;
		}
		if (!getNVar()) { return false; }							// the DB table pointer
		int i = NumericVarValues.get(theValueIndex).intValue();
		if (i == 0) {												// If pointer is zero
			RunTimeError(errStr);									// DB has been closed
			return false;
		}
		if (i < 0 || i > DataBases.size()) {
			RunTimeError("Invalid Database Pointer at:");
			return false;
		}
		return true;
	}

	private boolean getVarAndDbPtrArgs(int[] args) {				// first arg of command is a numeric variable
																	// and second is a DB table pointer
		String errStr = "Database not opened at:";
		if (DataBases.isEmpty()){
			RunTimeError(errStr);
			return false;
		}
		if (!getNVar()) { return false; }							// user's nvar
		args[0] = theValueIndex;

		if (!isNext(',')) { return false; }
		if (!getNVar()) { return false; }							// the DB table pointer
		args[1] = theValueIndex;

		int i = NumericVarValues.get(theValueIndex).intValue();
		if (i == 0) {												// If pointer is zero
			RunTimeError(errStr);									// DB has been closed
			return false;
		}
		if (i < 0 || i > DataBases.size()) {
			RunTimeError("Invalid Database Pointer at:");
			return false;
		}
		return true;
	}

	private boolean getVarAndCursorPtrArgs(int[] args) {			// first arg of command is a numeric variable
																	// and second is a DB cursor pointer
		if (Cursors.isEmpty()) {									// Make sure there is at least one cursor
			RunTimeError("Cursor not available at:");
			return false;
		}
		if (!getNVar()) { return false; }							// user's nvar
		args[0] = theValueIndex;

		if (!isNext(',')) { return false; }
		if (!getNVar()) { return false; }							// the DB cursor pointer
		args[1] = theValueIndex;

		int i = NumericVarValues.get(theValueIndex).intValue();
		if (i == 0) {												// If pointer is zero
			RunTimeError("Cursor done at:");						// then cursor is used up
			return false;
		}
		if (i < 0 || i > Cursors.size()) {
			RunTimeError("Invalid Cursor Pointer at:");
			return false;
		}
		return true;
	}

	private boolean getColumnValuePairs(ContentValues values) {		// Get column/value pairs from user command
		if (!isNext(',')) { return false; }
		do {
			if (!getStringArg()) { return false; }					// Column
			String Column = StringConstant;
			if (!isNext(',') || !getStringArg()) { return false; }	// Value
			String Value = StringConstant;
			values.put(Column, Value);								// Store the pair
		} while (isNext(','));
		return true;
	}

	private boolean execute_sql_open(){

		if (!getNVar()) return false;								// DB Pointer Variable
		int SaveValueIndex = theValueIndex;							// for the DB table pointer

		if (!isNext(',')) return false;
		if (!getStringArg()) return false;							// Get Data Base Name
		String DBname = StringConstant;
		if (!checkEOL()) return false;

		String fn;
		if (DBname.startsWith(":")) {
			fn = DBname;
		} else {
			if (!checkSDCARD('w')) return false;
			fn = Basic.getDataBasePath(DBname);
		}

		SQLiteDatabase db;
		try{														// Do the open or create
			db = SQLiteDatabase.openOrCreateDatabase(new File(fn), null );
		} catch  (Exception e) {
			return RunTimeError("SQL Exception: "+e);
		}

		// The newly opened data base is added to the DataBases list.
		// The list index of the new data base is added returned to the user

		NumericVarValues.set(SaveValueIndex, (double) DataBases.size()+1);
		DataBases.add(db);
		return true;
	}

		private boolean execute_sql_close(){
		
			if (!getDbPtrArg()) return false;						// get variable for the DB table pointer
			int i = NumericVarValues.get(theValueIndex).intValue();
			if (!checkEOL()) return false;
			
			SQLiteDatabase db = DataBases.get(i-1);					// get the data base
		   try {
			   db.close();											// Try closing it
		   }catch (Exception e) {
			   return RunTimeError(e);
		   }
		   
		   NumericVarValues.set(theValueIndex, 0.0);				// Set the pointer to 0 to indicate closed.
		   
    	   return true;
    		  
    	  }

		private boolean execute_sql_insert(){
		
			if (!getDbPtrArg()) return false;						// get variable for the DB table pointer
			int i = NumericVarValues.get(theValueIndex).intValue();
			SQLiteDatabase db = DataBases.get(i-1);					// get the data base

			if (!isNext(',')) return false;
			if (!getStringArg()) return false;						// Table Name
			String TableName = StringConstant;

			ContentValues values = new ContentValues();
			if (!getColumnValuePairs(values)) return false;			// Get column/value pairs from user command

			if (!checkEOL()) return false;

		   try {													// Now insert the pairs into the named table
	        db.insertOrThrow(TableName, null, values);
		   }catch (Exception e) {
			   return RunTimeError(e);
		   }

		   return true;
   	   }

		private boolean execute_sql_query(){
		
			int[] args = new int[2];							// Get the first two args:
			if (!getVarAndDbPtrArgs(args)) return false;
			int SaveValueIndex = args[0];						// Query Cursor Variable
			int DbTablePointerIndex = args[1];					// DB table pointer

			int i = NumericVarValues.get(DbTablePointerIndex).intValue();
			SQLiteDatabase db = DataBases.get(i-1);				// get the data base

			if (!isNext(',')) return false;
			if (!getStringArg()) return false;					// Table Name
			String TableName = StringConstant;
			
			if (!isNext(',')) return false;
			if (!getStringArg()) return false;					// String of comma separated columns to get
			String RawColumns = StringConstant;
		   
		   ArrayList<String> cList = new ArrayList<String>();	// Must convert string to an array of columns
		   														// starting by creating an Array List of column names

		   String cTemp = "";									// Parse the Raw Columns
		   for (int j=0; j<RawColumns.length(); ++j){
			   char t = RawColumns.charAt(j);
			   if (t!=','){                                     // while tossing out blanks
				   if ( t != ' ') 
				   {cTemp = cTemp + t;}
				   else{}	// add characters to the column name
			   }else{
				   cList.add(cTemp);							// comma terminates a column name, add name to array list
				   cTemp = "";									// and start a new column name
			   }
		   }
		   cList.add(cTemp);									// add last column to the list
		   
		   String []Q_Columns = new String[cList.size()];		// Finally, convert the array list
		   cList.toArray(Q_Columns);		   					// to a String Array.
		   
		   String Where = "";									// if no Where given, set empty
		   String Order = "";									// if no Order given, set empty
		   
			if (isNext(',')) {									// if no comma, then no optional Where
				if (!getStringArg()) return false;				// Where Value
				Where = StringConstant;

				if (isNext(',')) {								// if no comma, then no optional Order
					if (!getStringArg()) return false;			// Oder Value
					Order = StringConstant;
				}
			}
			if (!checkEOL()) return false;

		   Cursor cursor;
		   try{													// Do the query and get the cursor
	           cursor = db.query(TableName, Q_Columns, Where, null, null,
	              null, Order);
		   } catch (Exception e) {
			   return RunTimeError(e);
		   }
		   
		   NumericVarValues.set(SaveValueIndex, (double) Cursors.size()+1); // Save the Cursor index into the var
		   Cursors.add(cursor);												// and save the cursor.
		   
		   return true;
       }

		private boolean execute_sql_next(){

			int[] args = new int[2];							// Get the first two args:
			if (!getVarAndCursorPtrArgs(args)) return false;
			int SaveDoneIndex = args[0];						// Done Flag variable
			int SaveCursorIndex = args[1];						// DB Cursor pointer

			NumericVarValues.set(SaveDoneIndex, 0.00);			// set Not Done
			int i = NumericVarValues.get(SaveCursorIndex).intValue();
			Cursor cursor = Cursors.get(i-1);					// get the cursor

		   String result;
		   if (cursor.moveToNext()) {							// if there is another row, go there
			   for (int index = 0; isNext(','); ++index) {
				   if (!getSVar()) return false;				// Get next result variable
				   try {
						result = cursor.getString(index); 		// get the result
				   } catch (Exception e) {
						return RunTimeError(e);
				   }
				   if (result == null) { result = ""; }
				   StringVarValues.set(theValueIndex, result);	// set result into var
			   }
			   return checkEOL();

		   } else {												// no next row, cursor is used up
			   cursor.close();
			   NumericVarValues.set(SaveDoneIndex, 1.00);
			   NumericVarValues.set(SaveCursorIndex, 0.0);

			   return true;
		   }
       }

	private boolean execute_sql_query_length(){					// Report the number of rows in a query result
		int[] args = new int[2];								// Get the first two args:
		if (!getVarAndCursorPtrArgs(args)) return false;
		int ValueIndex = args[0];								// variable for number of rows
		int CursorIndex = args[1];								// DB Cursor pointer
		if (!checkEOL()) return false;

		int i = NumericVarValues.get(CursorIndex).intValue();
		Cursor cursor = Cursors.get(i - 1);						// get the cursor
		double nRows = cursor.getCount();
		NumericVarValues.set(ValueIndex, nRows);				// return number of rows to user
		return true;
	}

	private boolean execute_sql_query_position(){				// Report current position in query results
		int[] args = new int[2];								// Get the first two args:
		if (!getVarAndCursorPtrArgs(args)) return false;
		int ValueIndex = args[0];								// variable for position
		int CursorIndex = args[1];								// DB Cursor pointer
		if (!checkEOL()) return false;

		int i = NumericVarValues.get(CursorIndex).intValue();
		Cursor cursor = Cursors.get(i - 1);						// get the cursor
		double position = cursor.getPosition();
		NumericVarValues.set(ValueIndex, position + 1);			// return position to user, 1-based
		return true;
	}

	private boolean execute_sql_delete(){

		if (!getDbPtrArg()) return false;						// get variable for the DB table pointer
		int i = NumericVarValues.get(theValueIndex).intValue();
		SQLiteDatabase db = DataBases.get(i-1);					// get the data base

		if (!isNext(',')) return false;
		if (!getStringArg()) return false;						// Table Name
		String TableName = StringConstant;

		int returnValueIndex = 0;
		String Where = null;									// if no Where given, set null
		if (isNext(',')) {										// if no comma, then no optional Where
			if (!getStringArg()) return false;					// Where Value
			Where = StringConstant;
			if (isNext(',')) {									// if there's a where
				if (!getNVar()) return false;					// there can be a return value
				returnValueIndex = theValueIndex;
			}
		}
		if (!checkEOL()) return false;

		int count = 0;
		try {
			count = db.delete(TableName, Where, null);			// do the deletes
		} catch (Exception e) {
			return RunTimeError(e);
		}

		if (returnValueIndex != 0) {
			NumericVarValues.set(returnValueIndex, (double)count);
		}
		return true;
	}

		private boolean execute_sql_update() {

			if (!getDbPtrArg()) return false;					// get variable for the DB table pointer
			int i = NumericVarValues.get(theValueIndex).intValue();
			SQLiteDatabase db = DataBases.get(i-1);				// get the data base

			if (!isNext(',')) return false;
			if (!getStringArg()) return false;					// Table Name
			String TableName = StringConstant;

			ContentValues values = new ContentValues();
			if (!getColumnValuePairs(values)) return false;		// Get column/value pairs from user command

			String Where = null;								// Where is optional
			if (isNext(':')) {									// Colon indicates Where follows
				if (!getStringArg()) return false;				// Where Value
				Where = StringConstant;
			}
			if (!checkEOL()) return false;

		   try {
	        db.update(TableName, values, Where, null);
		   }catch (Exception e) {
				return RunTimeError(e);
		   }

    	   return true;
       }

		private boolean execute_sql_exec(){
			if (!getDbPtrArg()) return false;					// get variable for the DB table pointer
			int i = NumericVarValues.get(theValueIndex).intValue();
			SQLiteDatabase db = DataBases.get(i-1);				// get the data base

			if (!isNext(',')) return false;
			if (!evalStringExpression()) return false;			// Command string
			String CommandString = StringConstant;
			if (!checkEOL()) return false;

		   try {
		        db.execSQL(CommandString);
			   } catch (Exception e){
				   RunTimeError("SQL Exception: " + e);
				   return false;
			   }

    	   return true;
       }

		private boolean execute_sql_raw_query(){

			int[] args = new int[2];							// Get the first two args:
			if (!getVarAndDbPtrArgs(args)) return false;
			int SaveValueIndex = args[0];						// Query Cursor Variable
			int DbTablePointerIndex = args[1];					// DB table pointer

			int i = NumericVarValues.get(DbTablePointerIndex).intValue();
			SQLiteDatabase db = DataBases.get(i-1);				// get the data base

			if (!isNext(',')) return false;
			if (!getStringArg()) return false;					// Command string
			String QueryString = StringConstant;
			if (!checkEOL()) return false;

		   Cursor cursor;
		   try{													// Do the query and get the cursor
	           cursor = db.rawQuery(QueryString, null);
		   }catch (Exception e) {
				return RunTimeError(e);
		   }
		   
		   NumericVarValues.set(SaveValueIndex, (double) Cursors.size()+1); // Save the Cursor index into the var
		   Cursors.add(cursor);												// and save the cursor.

    	  return true;
      }

		private boolean execute_sql_drop_table(){

			if (!getDbPtrArg()) return false;					// get variable for the DB table pointer
			int i = NumericVarValues.get(theValueIndex).intValue();
			SQLiteDatabase db = DataBases.get(i-1);				// get the data base

			if (!isNext(',')) return false;
			if (!getStringArg()) return false;					// Table Name
			String TableName = StringConstant;
			if (!checkEOL()) return false;

			String CommandString = "DROP TABLE IF EXISTS " + TableName;

		   try {
		        db.execSQL(CommandString);
			   }catch (Exception e) {
					return RunTimeError(e);
			   }
		   
    	  return true;
      }

		private boolean execute_sql_new_table(){

			if (!getDbPtrArg()) return false;						// get variable for the DB table pointer
			int i = NumericVarValues.get(theValueIndex).intValue();
			SQLiteDatabase db = DataBases.get(i-1);					// get the data base

			if (!isNext(',')) return false;
			if (!getStringArg()) return false;			   			// Table Name
			String TableName = StringConstant;

			if (!isNext(',')) return false;
			ArrayList <String> Columns = new ArrayList<String>();
			do{
				if (!getStringArg()) return false;						// Columns
				Columns.add(StringConstant);
			} while (isNext(','));
			if (!checkEOL()) return false;

		   String columns = "";
		   int cc = Columns.size();
		   for (int j =0; j<cc; ++j){
			   columns = columns + Columns.get(j) + " TEXT";
			   if (j != cc-1) columns = columns +  " , ";
		   }
		   
		   String CommandString = StringConstant;	   
		   CommandString = "CREATE TABLE " + TableName + "( "
		   + "_id INTEGER PRIMARY KEY AUTOINCREMENT, " 
		   + columns + " )";

		   try {
		        db.execSQL(CommandString);
			   }catch (Exception e) {
					return RunTimeError(e);
			   }
		   
    	  
    	  return true;
      }

// **********************************************  Graphics Package **********************************************
   
   public  boolean executeGR(){
   	if (!GetGRKeyWord()){ return false;
   	}else if (!GRopen && KeyWordValue != gr_open) {
   		RunTimeError("Graphics not opened at:");
   		return false;
   		}
   		else{
   	    	  switch (KeyWordValue){
   	    	  	case gr_open:
   	    	  		if (!execute_gr_open()){return false;}
   	    	  		break;
   	    	  	case gr_render:
   	    	  		if (!execute_gr_render()){return false;}
   	    	  		break;
   	    	  	case gr_color:
   	    	  		if (!execute_gr_color()){return false;}
   	    	  		break;
   	    	  	case gr_point:
   	    	  		if (!execute_gr_point()){return false;}
   	    	  		break;
   	    	  	case gr_line:
   	    	  		if (!execute_gr_line()){return false;}
   	    	  		break;
   	    	  	case gr_rect:
   	    	  		if (!execute_gr_rect()){return false;}
   	    	  		break;
   	    	  	case gr_arc:
   	    	  		if (!execute_gr_arc()){return false;}
   	    	  		break;
   	    	  	case gr_circle:
   	    	  		if (!execute_gr_circle()){return false;}
   	    	  		break;
   	    	  	case gr_oval:
   	    	  		if (!execute_gr_oval()){return false;}
   	    	  		break;
   	    	  	case gr_cls:
   	    	  		if (!execute_gr_cls()){return false;}
   	    	  		break;
   	    	  	case gr_hide:
   	    	  		if (!execute_gr_hide()){return false;}
   	    	  		break;
   	    	  	case gr_show:
   	    	  		if (!execute_gr_show()){return false;}
   	    	  		break;
   	    	  	case gr_touch:
   	    	  		if (!execute_gr_touch(0)){return false;}
   	    	  		break;
   	    	  	case gr_touch2:
   	    	  		if (!execute_gr_touch(1)){return false;}
   	    	  		break;
  	    	  	case gr_text_draw:
   	    	  		if (!execute_gr_text_draw()){return false;}
   	    	  		break;
   	    	  	case gr_text_align:
   	    	  		if (!execute_gr_text_align()){return false;}
   	    	  		break;
   	    	  	case gr_text_size:
   	    	  		if (!execute_gr_text_size()){return false;}
   	    	  		break;
   	    	  	case gr_text_underline:
   	    	  		if (!execute_gr_text_underline()){return false;}
   	    	  		break;
   	    	  	case gr_text_skew:
   	    	  		if (!execute_gr_text_skew()){return false;}
   	    	  		break;
   	    	  	case gr_text_bold:
   	    	  		if (!execute_gr_text_bold()){return false;}
   	    	  		break;
   	    	  	case gr_text_strike:
   	    	  		if (!execute_gr_text_strike()){return false;}
   	    	  		break;
   	    	  	case gr_bitmap_load:
   	    	  		if (!execute_gr_bitmap_load()){return false;}
   	    	  		break;
   	    	  	case gr_bitmap_scale:
   	    	  		if (!execute_gr_bitmap_scale()){return false;}
   	    	  		break;
   	    	  	case gr_bitmap_draw:
   	    	  		if (!execute_gr_bitmap_draw()){return false;}
   	    	  		break;
   	    	  	case gr_rotate_start:
   	    	  		if (!execute_gr_rotate_start()){return false;}
   	    	  		break;
   	    	  	case gr_rotate_end:
   	    	  		if (!execute_gr_rotate_end()){return false;}
   	    	  		break;
   	    	  	case gr_modify:
   	    	  		if (!execute_gr_modify()){return false;}
   	    	  		break;
   	    	  	case gr_orientation:
   	    	  		if (!execute_gr_orientation()){return false;}
   	    	  		break;
   	    	  	case gr_screen:
   	    	  		if (!execute_gr_screen()){return false;}
   	    	  		break;
   	    	  	case gr_close:
   	    	  		if (!execute_gr_close()){return false;}
   	    	  		break;
   	    	  	case gr_front:
   	    	  		if (!execute_gr_front()){return false;}
   	    	  		break;
   	    	  	case gr_bound_touch:
   	    	  		if (!execute_gr_bound_touch(0)){return false;}
   	    	  		break;
   	    	  	case gr_bound_touch2:
   	    	  		if (!execute_gr_bound_touch(1)){return false;}
   	    	  		break;
   	    	  	case gr_bitmap_size:
   	    	  		if (!execute_gr_bitmap_size()){return false;}
   	    	  		break;
   	    	  	case gr_bitmap_delete:
   	    	  		if (!execute_gr_bitmap_delete()){return false;}
   	    	  		break;
   	    	  	case gr_set_pixels:
   	    	  		if (!execute_gr_set_pixels()){return false;}
   	    	  		break;
   	    	  	case gr_get_pixel:
   	    	  		if (!execute_gr_get_pixel()){return false;}
   	    	  		break;
   	    	  	case gr_save:
   	    	  		if (!execute_gr_save()){return false;}
   	    	  		break;
   	    	  	case gr_text_width:
   	    	  		if (!execute_gr_text_width()){return false;}
   	    	  		break;
   	    	  	case gr_scale:
   	    	  		if (!execute_gr_scale()){return false;}
   	    	  		break;
   	    	  	case gr_getdl:
   	    	  		if (!execute_gr_getdl()){return false;}
   	    	  		break;
   	    	  	case gr_newdl:
   	    	  		if (!execute_gr_newdl()){return false;}
   	    	  		break;
   	    	  	case gr_clip:
   	    	  		if (!execute_gr_clip()){return false;}
   	    	  		break;
   	    	  	case gr_bitmap_crop:
   	    	  		if (!execute_gr_bitmap_crop()){return false;}
   	    	  		break;
   	    	  	case gr_stroke_width:
   	    	  		if (!execute_gr_stroke_width()){return false;}
   	    	  		break;
   	    	  	case gr_poly:
   	    	  		if (!execute_gr_poly()){return false;}
   	    	  		break;
   	    	  	case gr_statusbar_show:
   	    	  		if (!execute_statusbar_show()){return false;}
   	    	  		break;
   	    	  	case gr_bitmap_save:
   	    	  		if (!execute_bitmap_save()){return false;}
   	    	  		break;
   	    	  	case gr_camera_shoot:
   	    	  		if (!execute_camera_shoot(CameraView.PICTURE_MODE_USE_UI)){return false;}
   	    	  		break;
   	    	  	case gr_camera_autoshoot:
   	    	  		if (!execute_camera_shoot(CameraView.PICTURE_MODE_AUTO)){return false;}
   	    	  		break;
   	    	  	case gr_camera_manualshoot:
   	    	  		if (!execute_camera_shoot(CameraView.PICTURE_MODE_MANUAL)){return false;}
   	    	  		break;
   	    	  	case gr_camera_blindshoot:
   	    	  		if (!execute_camera_shoot(CameraView.PICTURE_MODE_BLIND)){return false;}
   	    	  		break;
   	    	  	case gr_screen_to_bitmap:
   	    	  		if (!execute_screen_to_bitmap()){return false;}
   	    	  		break;
   	    	  	case gr_paint_get:
   	    	  		if (!execute_paint_get()){return false;}
   	    	  		break;
   	    	  	case gr_brightness:
   	    	  		if (!execute_brightness()) return false;
   	    	  		break;
   	    	  	case gr_bitmap_create:
   	    	  		if (!execute_gr_bitmap_create()) return false;
   	    	  		break;
   	    	  	case gr_bitmap_drawinto_start:
   	    	  		if (!execute_gr_bitmap_drawinto_start()) return false;
   	    	  		break;
   	    	  	case gr_bitmap_drawinto_end:
   	    	  		if (!execute_gr_bitmap_drawinto_end()) return false;
   	    	  		break;
   	    	  	case gr_get_position:
   	    	  		if (!execute_gr_get_position()) return false;
   	    	  		break;
   	    	  	case gr_get_bmpixel:
   	    	  		if (!execute_gr_get_bmpixel()) return false;
   	    	  		break;
   	    	  	case gr_get_value:
   	    	  		if (!execute_gr_get_value()) return false;
   	    	  		break;
   	    	  	case gr_get_type:
   	    	  		 return execute_gr_get_type();
   	    	  	case gr_get_params:
  	    	  		 return execute_gr_get_params();
   	    	  	case gr_antialias:
   	    	  		if (!execute_gr_antialias()) return false;
   	    	  		break;
   	    	  	case gr_get_textbounds:
   	    	  		if (!execute_gr_get_texbounds()) return false;
   	    	  		break;
   	    	  	case gr_text_typeface:
   	    	  		if (!execute_gr_text_typeface()) return false;
   	    	  		break;
   	    	  	case gr_ontouch_resume:
   	    	  		if (!execute_gr_touch_resume()) return false;
   	    	  		break;
   	    	  	case gr_camera_select:
   	    	  		if (!execute_gr_camera_select()) return false;
   	    	  		break;
   	    	  		
   	    	  	default:
   	    	  		return false;
   	    	  }
   	    	  return true;
   	      }
   	   }
 
	private boolean GetGRKeyWord(){							// Get a GR command keyword if it is there
															// is the current line index at a keyword?
		for (int i = 0; i < GR_KW.length; ++i) {			// loop through the keyword list
			if (ExecutingLineBuffer.startsWith(GR_KW[i], LineIndex)) { // if there is a match
				KeyWordValue = i;							// set the keyword number
				LineIndex += GR_KW[i].length();				// move the line index to end of keyword
				return true;								// and report back
			}
		}
		KeyWordValue = gr_none;								// no keyword found
		return false;										// report fail
	}

	  public void DisplayListAdd(Bundle b){
		  b.putInt("alpha", 256);
		  b.putInt("paint", PaintList.size()-1);							// paint for this lines
		  
		  if (drawintoCanvas != null){
			  GR.drawView.doDraw(drawintoCanvas, b);
			  return;
		  }

		  RealDisplayList.add(DisplayList.size());
		  DisplayList.add(b);
	  }
	  
	  public boolean execute_gr_bitmap_drawinto_start(){
		   if (!evalNumericExpression()) return false;					// 
		   if (!checkEOL()) return false;
		   int q = EvalNumericExpressionValue.intValue();
		   if (q<1 | q >= BitmapList.size()){
			   RunTimeError("Invalid Bitmap Pointer");
			   return false;			   
		   }
		   Bitmap SrcBitMap = BitmapList.get(q);
		   if (SrcBitMap == null){
			   RunTimeError("Bitmap was deleted");
			   return false;
		   }
		   if (!SrcBitMap.isMutable()){
			   RunTimeError("Bitmaps loaded from files not changeable.");
			   return false;
		   }
		   
		   if (SrcBitMap.isRecycled()){
			   RunTimeError("Bitmap was recycled");
			   SrcBitMap = null;
			   return false;
		   }
		   drawintoCanvas = new Canvas(SrcBitMap);
		   SrcBitMap = null;
		  
		  return true;
	  }
	  
	  public boolean execute_gr_bitmap_drawinto_end(){
		  drawintoCanvas = null;
		  return true;
	  }
	  
	  
	  public  void DisplayListClear(){
//			StopDisplay = true;											// Sigmal GR to stop display
//	    	try {Thread.sleep(500);}catch(InterruptedException e){}     // Give GR some time to do it
//		BitmapList.clear();
//		BitmapList.add(null);                             // Set Zero entry as null

	  	DisplayList.clear();										// Clear the Display List
	  	RealDisplayList.clear();
	  	PaintList.clear();											// and the Paint list
	    PaintListAdd(aPaint);								        // Add dummy element 0

	  	Bundle b = new Bundle();									// Create a new Display list
          b.putInt("type", GR.dNull);								// with a null entry

		   Paint tPaint = newPaint(aPaint);							// Create a newPaint object
		   tPaint.setARGB(255, 0, 0, 0);							// Set the colors, etc
		   tPaint.setAntiAlias(true);
		   tPaint.setStyle(Paint.Style.FILL );
		   tPaint.setStrokeWidth(0);
		   int f= tPaint.getFlags();
//		   tPaint.setFlags(f | Paint.FILTER_BITMAP_FLAG);
		   aPaint = newPaint(tPaint);							// Copy the temp paint to aPaint
	       PaintListAdd(aPaint);								// Add the newPaint to the Paint List

	       DisplayListAdd(b);

//    	StopDisplay = false;										// Tell GR it can start displaying again
	  }

	private boolean execute_gr_getdl(){

		String var = getArrayVarForWrite(TYPE_NUMERIC);				// get the result array variable
		if (var == null) { return false; }							// must name a new numeric array variable

		boolean keepHiddenObjects = false;
		if (isNext(',')) {											// Optional "hidden" flag
			if (!evalNumericExpression()) { return false; }
			keepHiddenObjects = (EvalNumericExpressionValue != 0.0);
		}
		if (!checkEOL()) { return false; }							// line must end with ']'

		double[] list = new double[RealDisplayList.size() + 1];
		int count = 0;
		for (int idx : RealDisplayList) {							// For each object index
			boolean include = ((idx != 0) &&						// If not index of null object...
				(keepHiddenObjects ||								// ... and either keeping all objects...
					(DisplayList.get(idx).getInt("hide") == 0)));	// ... or object is not hidden...
			if (include) { list[count++] = idx; }					// ... then put index in the new list
		}
		if (count == 0) { count = 1; }								// if no objects, make a list with
																	// one entry that indexes the null object

		if (!BuildBasicArray(var, true, count)) { return false; }	// build the array
		for (int i = 0, j = ArrayValueStart; i < count; ++i, ++j) {	// stuff the array
			NumericVarValues.set(j, list[i]);						// count may be < list.length
		}
		return true;
	}

	private boolean execute_gr_newdl(){

		if (getArrayVarForRead() == null) { return false; }			// Get the array variable
		if (!VarIsNumeric) { return RunTimeError(EXPECT_NUM_ARRAY); } // Insure that it is a numeric array
		int arrayTableIndex = VarIndex.get(VarNumber);

		Integer[] p = { null, null };
		if (!getIndexPair(p)) return false;							// Get values inside [], if any
		if (!checkEOL()) { return false; }							// line must end with ']'

		if (!getArraySegment(arrayTableIndex, p)) { return false; }	// Get array base and length
		int base = p[0].intValue();
		int length = p[1].intValue();

		RealDisplayList.clear();
		RealDisplayList.add(0);										// First entry points to null bundle

		for (int i = 0; i < length; ++i) {							// Copy the bundle pointers 
			int id = NumericVarValues.get(base + i).intValue();
			if (id < 0 || id >= DisplayList.size()) {
				return RunTimeError("Invalid Object Number");
			}
			RealDisplayList.add(id);
		}

		return true;
	}

	  public void PaintListAdd(Paint p){
		  PaintList.add(p);
	  }
	  
	  public Paint newPaint(Paint fromPaint){				// Does a new Paint
		  Typeface tf = fromPaint.getTypeface();			// while prsereving the type face
		  Paint rPaint = new Paint(fromPaint);
		  rPaint.setTypeface(tf);
		  return rPaint;
	  }

	private boolean execute_gr_open(){
		if (GRopen) {
			return RunTimeError("Graphics already Opened");
		}

		if (!evalNumericExpression()) return false;							// Get alpha
		int a = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get red
		int r = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get green
		int g = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get blue
		int b = EvalNumericExpressionValue.intValue();

		ShowStatusBar = 0;
		GRorientation = 0;
		if (isNext(',')) {
			if (!evalNumericExpression()) return false;
			ShowStatusBar = EvalNumericExpressionValue.intValue();
			if (isNext(',')) {
				if (!evalNumericExpression()) return false;
				GRorientation = EvalNumericExpressionValue.intValue();
			}
		}
		if (!checkEOL()) return false;

			  GR.theBackGround = a * 0x1000000 +									// Set the appropriate bytes
			  					 r * 0x10000 +
			  					 g * 0x100 +
			  					 b ;

		  drawintoCanvas = null;
		  DisplayListClear();
		  BitmapList.clear();
		  BitmapList.add(null);                             // Set Zero entry as null

		   Paint tPaint = new Paint();					// Create a newPaint object
		   tPaint.setARGB(a, r, g, b);							// Set the colors, etc
		   tPaint.setAntiAlias(true);
		   tPaint.setStyle(Paint.Style.FILL );
		   tPaint.setStrokeWidth(0f);
		   int f= tPaint.getFlags();
//		   tPaint.setFlags(f | Paint.FILTER_BITMAP_FLAG);
		   aPaint = newPaint(tPaint);							// Copy the temp paint to aPaint
	       PaintListAdd(aPaint);								// Add the newPaint to the Paint List as element 1

		  GraphicsPaused = false;							// Set up the signals
		  GRrunning = false;
     	  GRclass = new Intent(this, GR.class);				// Start the Graphics
     	  startActivityForResult(GRclass, BASIC_GENERAL_INTENT);
	       while (!GRrunning) Thread.yield();				// Do not continue until GR signals it is running
	       background = false;
	       GRopen = true;									// Set some more signals
	       RunPaused = false;
	       NewTouch[0] = false;
	       NewTouch[1] = false;
	       NewTouch[2] = false;
	       GR.doSTT = false;
	       GRFront = true;
	       CameraNumber = -1;
	       setVolumeControlStream(AudioManager.STREAM_MUSIC);
		  return true;
	  }
	  
	  private boolean execute_paint_get(){
		  if (!getNVar()) return false;
		  NumericVarValues.set(theValueIndex, (double) PaintList.size()-1);
		  return true;
	  }
	  
	  private boolean execute_gr_close(){
			if (!checkEOL()) return false;
		  
		  DisplayListClear();                 // Clear the existing display list
		  
		  Bundle aBundle = new Bundle();	  // Create a new display list bundle
		  aBundle.putInt("type", GR.dClose);  // which commands GR.java to close
		  aBundle.putInt("hide", 0);
		  DisplayListAdd(aBundle);
		  
			synchronized (GR.Rendering) {
				if (GR.Rendering) return true;
				GR.Rendering = true;
			}

		  GR.drawView.postInvalidate();		  // Start the draw so the command will get executed.
		  GRopen = false;
		  GRFront = false;
		  return true;
	  }
	  
	  private boolean execute_gr_render(){
			if (!checkEOL()) return false;
		  if (GR.drawView == null){				// Make sure drawView has not gone null
			  Stop = true;
			  return false;
		  }
		  
		synchronized (GR.Rendering) {
			if (GR.Rendering) return true;
			GR.Rendering = true;
		}
		  
		  GR.NullBitMap = false;
		  
		  GR.drawView.postInvalidate();			// Start GR drawing.
		  while (GR.Rendering)
		  	{Thread.yield();}

		  if (GR.NullBitMap){
			  RunTimeError("Display List had deleted bitmap.");
			  GR.NullBitMap = false;
			  return false;
		  }
		  return true;
	  }

	private boolean execute_gr_color(){

		if (!evalNumericExpression()) return false;							// Get alpha
		int a = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get red
		int r = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get green
		int g = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get blue
		int b = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get fill
		double d = EvalNumericExpressionValue;
		if (!checkEOL()) return false;

		   Paint tPaint = newPaint(aPaint);					// Create a newPaint object
		   tPaint.setARGB(a, r, g, b);							// Set the colors, etc
//		   tPaint.setAntiAlias(true);
		   if (d == 0) tPaint.setStyle(Paint.Style.STROKE );
		   else if (d == 1) tPaint.setStyle(Paint.Style.FILL);
		   else tPaint.setStyle(Paint.Style.FILL_AND_STROKE);
		   aPaint = newPaint(tPaint);							// Copy the temp paint to aPaint
	       PaintListAdd(aPaint);								// Add the newPaint to the Paint List
		  return true;
	}

	private boolean execute_gr_antialias(){
		if (!evalNumericExpression()) return false;				// Get the boolean
		Paint tPaint = newPaint(aPaint);
		tPaint.setAntiAlias(EvalNumericExpressionValue != 0);
		aPaint = newPaint(tPaint);								// Copy the temp paint to aPaint
		PaintListAdd(aPaint);									// Add the newPaint to the Paint List
		return true;
	}

	private boolean execute_gr_stroke_width(){
		if (!evalNumericExpression()) return false;				// Get the width
		float width = EvalNumericExpressionValue.floatValue();
		if (width < 0) {
			return RunTimeError("Width must be >= 0");
		}
		Paint tPaint = newPaint(aPaint);						// Create a newPaint object
		tPaint.setStrokeWidth(width);							// Set the stroke width
		aPaint = newPaint(tPaint);								// Copy the temp paint to aPaint
		PaintListAdd(aPaint);									// Add the newPaint to the Paint List

		return true;
	}

	private boolean execute_gr_point(){
		Bundle aBundle = new Bundle();
		aBundle.putInt("type", GR.dPoint);
		aBundle.putInt("hide", 0);

		if (!getNVar()) return false;							// Graphic Object Variable
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;				// Get x
		int x = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;				// Get y
		int y = EvalNumericExpressionValue.intValue();
		if (!checkEOL()) return false;

		aBundle.putInt("x", x);
		aBundle.putInt("y", y);
		int p = PaintList.size();								// Set the most current paint as the paint
		aBundle.putInt("paint", p-1);							// paint for this point
		NumericVarValues.set(SaveValueIndex, (double) DisplayList.size()); 	// Save the GR Object index into the var

		DisplayListAdd(aBundle);								// Add the line to the display list
		return true;
	}

	private boolean execute_gr_line(){
		  Bundle aBundle = new Bundle();
		  aBundle.putInt("type", GR.dLine);
		  aBundle.putInt("hide", 0);

		if (!getNVar()) return false;										// Graphic Object Variable
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get x1
		aBundle.putInt("x1", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get y1
		aBundle.putInt("y1", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get x2
		aBundle.putInt("x2", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get y2
		aBundle.putInt("y2", EvalNumericExpressionValue.intValue());
		if (!checkEOL()) return false;


		  int p = PaintList.size();								// Set the most current paint as the paint
		  aBundle.putInt("paint", p-1);							// paint for this lines
		  NumericVarValues.set(SaveValueIndex, (double) DisplayList.size()); 	// Save the GR Object index into the var

		  DisplayListAdd(aBundle);				// Add the line to the display list
		  return true;
	}

	private boolean execute_gr_rect(){
		  Bundle aBundle = new Bundle();
		  aBundle.putInt("type", GR.dRect);
		  aBundle.putInt("hide", 0);

		if (!getNVar()) return false;										// Graphic Object Variable
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get left
		aBundle.putInt("left", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get top
		aBundle.putInt("top", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get right
		aBundle.putInt("right", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get bottom
		aBundle.putInt("bottom", EvalNumericExpressionValue.intValue());
		if (!checkEOL()) return false;

		  int p = PaintList.size();						// Set current paint as this circle's paint
		  aBundle.putInt("paint", p-1);
		  NumericVarValues.set(SaveValueIndex, (double) DisplayList.size()); 	// Save the GR Object index into the var

		  DisplayListAdd(aBundle);
		  return true;
	}

	private boolean execute_gr_arc(){
		  Bundle aBundle = new Bundle();
		  aBundle.putInt("type", GR.dArc);
		  aBundle.putInt("hide", 0);

		if (!getNVar()) return false;										// Graphic Object Variable
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get left
		aBundle.putInt("left", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get top
		aBundle.putInt("top", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get right
		aBundle.putInt("right", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get bottom
		aBundle.putInt("bottom", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get start angle
		aBundle.putInt("start_angle", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get end angle
		aBundle.putInt("sweep_angle", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get flag
		aBundle.putInt("fill_mode", EvalNumericExpressionValue.intValue());
		if (!checkEOL()) return false;

		  int p = PaintList.size();						// Set the current paint as this objct's paint
		  aBundle.putInt("paint", p-1);
		  NumericVarValues.set(SaveValueIndex, (double) DisplayList.size()); 	// Save the GR Object index into the var

		  DisplayListAdd(aBundle);
		  return true;
	}

	private boolean execute_gr_circle(){
		  Bundle aBundle = new Bundle();
		  aBundle.putInt("type", GR.dCircle);
		  aBundle.putInt("hide", 0);

		if (!getNVar()) return false;										// Graphic Object Variable
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get x
		aBundle.putInt("x", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get x
		aBundle.putInt("y", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get r
		aBundle.putInt("radius", EvalNumericExpressionValue.intValue());
		if (!checkEOL()) return false;

		  int p = PaintList.size();					// Set the current paint as this object's paint
		  aBundle.putInt("paint", p-1);
		  NumericVarValues.set(SaveValueIndex, (double) DisplayList.size()); // Save the GR Object index into the var
		  DisplayListAdd(aBundle);
		  return true;
	}

	private boolean execute_gr_oval(){
		  Bundle aBundle = new Bundle();
		  aBundle.putInt("type", GR.dOval);
		  aBundle.putInt("hide", 0);

		if (!getNVar()) return false;										// Graphic Object Variable
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get left
		aBundle.putInt("left", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get top
		aBundle.putInt("top", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get right
		aBundle.putInt("right", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get bottom
		aBundle.putInt("bottom", EvalNumericExpressionValue.intValue());
		if (!checkEOL()) return false;

		  int p = PaintList.size();						// Set the current paint as this object's paint
		  aBundle.putInt("paint", p-1);
		  NumericVarValues.set(SaveValueIndex, (double) DisplayList.size()); 	// Save the GR Object index into the var
		  DisplayListAdd(aBundle);
		  return true;
	}

	  private double gr_collide(int Object1, int Object2){

		  double fail = -1;
		  double xfalse = 0;
		  double xtrue = 1;
		  
		  if (Object1 <0 || Object1 >= DisplayList.size()){
			  RunTimeError("Object 1 Number out of range");
			  return fail;
		  }
		  Bundle b1 = DisplayList.get(Object1);									// Get the bundle to change
		  if (b1.getInt("hide") != 0) return xfalse;								// If hidden then no collide
		  Rect r1 = gr_getRect(b1);
		  if (r1 == null) return fail;
		  
		  if (Object2 <0 || Object2 >= DisplayList.size()){
			  RunTimeError("Object 2 Number out of range");
			  return fail;
		  }
		  Bundle b2 = DisplayList.get(Object2);									// Get the bundle to change
		  if (b2.getInt("hide") != 0) return xfalse;								// If hidden then no collide
		  Rect r2 = gr_getRect(b2);
		  if (r2 == null) return fail;
		  
		  if (r1.bottom < r2.top)				// Test for collision
			  return xfalse;
			if (r1.top > r2.bottom)
			  return xfalse;
			if (r1.right < r2.left)
			  return xfalse;
			if (r1.left > r2.right)
			  return xfalse;

		  return xtrue;
	  }

	  private Rect gr_getRect(Bundle b){
		  Rect theRect = null;
		  
		  int type = b.getInt("type");
     		switch (type) {
    		case GR.dCircle:
                theRect = new Rect();
    			int cx= b.getInt("x");
    			int cy =b.getInt("y");
    			int cr = b.getInt("radius");
    			theRect.top = cy - cr;
    			theRect.left = cx - cr;
    			theRect.bottom = cy + cr;
    			theRect.right = cx + cr;
    			break;

    		case GR.dRect:
    		case GR.dOval:
    		case GR.dArc:
                theRect = new Rect();
                theRect.left= b.getInt("left");
                theRect.top =b.getInt("top");
                theRect.bottom = b.getInt("bottom");
    			theRect.right =b.getInt("right");
    			break;

    		case GR.dBitmap:
                theRect = new Rect();
    			theRect.top = b.getInt("y");
    			theRect.left = b.getInt("x");
    			Bitmap theBitmap = BitmapList.get(b.getInt("bitmap"));
    			theRect.bottom = theRect.top + theBitmap.getHeight();
    			theRect.right = theRect.left + theBitmap.getWidth();
                break;
     		}


		  return theRect;
	  }

	  private boolean execute_gr_cls(){
			if (!checkEOL()) return false;
		  
		  DisplayListClear();
		  return true;
	  }

	private Bundle getObjectBundle() {						// get and validate the user-sepecified graphics object
		Bundle b = null;
		if (!evalNumericExpression()) return b;					// Get Object Number
		int obj = EvalNumericExpressionValue.intValue();
		if (obj < 0 || obj >= DisplayList.size()) {
			RunTimeError("Object out of range");
		} else {
			b = DisplayList.get(obj);							// Get the specified display object
		}
		return b;
	}

	private boolean execute_gr_hide(){
		if (!evalNumericExpression()) return false;							// Get Object Number
		int obj = EvalNumericExpressionValue.intValue();
		if (obj < 0 || obj >= DisplayList.size()) {
			return RunTimeError("Hide parameter out of range");
		}
		if (!checkEOL()) return false;

		Bundle b = DisplayList.get(obj);				// Get the specified display object
		b.putInt("hide", 1);							// Set hide to true
		DisplayList.set(obj, b);						// put the modified object back
		return true;
	}

	private boolean execute_gr_show(){
		if (!evalNumericExpression()) return false;							// Get Object Number
		int obj = EvalNumericExpressionValue.intValue();
		if (obj < 0 || obj >= DisplayList.size()) {
			return RunTimeError("Show parameter out of range");
		}
		if (!checkEOL()) return false;

		Bundle b = DisplayList.get(obj);				// Get the specified display object
		b.putInt("hide", 0);							// Set hide to false
		DisplayList.set(obj, b);						// put the modified object back
		return true;
	}

	private boolean execute_gr_get_position(){
		Bundle b = getObjectBundle();
		if (b == null) return false;

		if (!isNext(',') || !getNVar()) return false;
		int xIndex = theValueIndex;
		if (!isNext(',') || !getNVar()) return false;
		int yIndex = theValueIndex;
		if (!checkEOL()) return false;

		     int x = 0;									// get the position value
		     int y = 0;
		     if (b.containsKey("x")) x = b.getInt("x");
		     else x = b.getInt("left");
		     if (b.containsKey("y")) y = b.getInt("y");
		     else y = b.getInt("top");

		NumericVarValues.set(xIndex, (double) x);
		NumericVarValues.set(yIndex, (double) y);

		return true;
	}

	private boolean execute_gr_get_value(){
		Bundle b = getObjectBundle();								// get the graphics object
		if (b == null) return false;

		if (!isNext(',') || !getStringArg()) return false;			// get the parameter string
		String parm = StringConstant;

		if (!b.containsKey(parm)) {
			return RunTimeError("Object does not contain " + parm);
		}

		if (!isNext(',') || !getVar() || !checkEOL()) return false;
		if (VarIsNumeric == parm.equals("text")) {					// error if numeric var and "text" tag
			return RunTimeError("Wrong var type for tag: " + parm);	// or string var and not "text" tag
		}
		if (VarIsNumeric) {
			int value = b.getInt(parm);
			NumericVarValues.set(theValueIndex, (double) value);
		} else {
			String theText = b.getString(parm);
			StringVarValues.set(theValueIndex, theText);
		}
		return true;
	}

	private boolean execute_gr_get_type() {
		Bundle b = getObjectBundle();								// get the graphics object
		if (b == null) return false;
		if (!isNext(',') || !getStringArg() || !checkEOL()) return false;// var for type string

		int type = b.getInt("type");
		StringVarValues.set(theValueIndex, GR.types[type]);
		return true;
	}

	private boolean execute_gr_get_params() {
		Bundle b = getObjectBundle();								// get the graphics object
		if (b == null) return false;

		if (!isNext(',')) { return false; }
		String var = getArrayVarForWrite(TYPE_STRING);				// get the result array variable
		if (var == null) { return false; }							// must name a new string array variable
		if (!checkEOL()) { return false; }							// line must end with ']'

		Set<String> keySet = b.keySet();							// get parameters from Bundle
		keySet.remove("type");
		keySet.remove("hide");
		ArrayList<String> keys = new ArrayList<String>(keySet);

		/* Puts the list of keys into a new array */
		return ListToBasicStringArray(var, keys, keys.size());
	}

	private boolean execute_gr_touch(int p){
		if (!getNVar()) return false;							// Graphic boolean Variable
		int SaveBooleanIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!getNVar()) return false;							// Graphic X variable
		int SaveXIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!getNVar()) return false;							// Graphic Y variable
		int SaveYIndex = theValueIndex;
		if (!checkEOL()) return false;

		   double flag = 0.0;
		   if (NewTouch[p]) {					// If touched
			   flag = 1;						// set flag to tue
//			   GR.NewTouch[p] = false;
		   }
		   
		   NumericVarValues.set(SaveBooleanIndex, flag);  // Return the values to the user
		   NumericVarValues.set(SaveXIndex, TouchX[p]);
		   NumericVarValues.set(SaveYIndex, TouchY[p]);
		   return true;
	}

	private boolean execute_gr_bound_touch(int p){
		if (!getNVar()) return false;							// Graphic boolean Variable
		int SaveBooleanIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;				// Get left
		double left = EvalNumericExpressionValue;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;				// Get top
		double top = EvalNumericExpressionValue;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;				// Get right
		double right = EvalNumericExpressionValue;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;				// Get bottom
		double bottom = EvalNumericExpressionValue;
		if (!checkEOL()) return false;

			   boolean flag = false;
			   if (NewTouch[p]) {					// If touched
				   flag = true;						// set flag to true
//				   GR.NewTouch[p] = false;				// then set not touched
			   }

			   if (!flag){                                       // If not touched
				   NumericVarValues.set(SaveBooleanIndex, 0.0);  // Return the false boolean to the user
				   return true;
			   }
			   
			   if (TouchX[p] >= left && TouchX[p] <= right &&      // If the touch was in the bounding rect
			       TouchY[p] >= top  && TouchY[p] <= bottom) {
				   NumericVarValues.set(SaveBooleanIndex, 1.0);    // Return the true boolean to the user
			   } else NumericVarValues.set(SaveBooleanIndex, 0.0); // else return the false boolean to the user

		return true;
	}

	private boolean execute_gr_text_draw(){
		  Bundle aBundle = new Bundle();       // Create a new object of type text
		  aBundle.putInt("type", GR.dText);
		  aBundle.putInt("hide", 0);

		if (!getNVar()) return false;										// Graphic Object Variable
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get x
		aBundle.putInt("x", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get y
		aBundle.putInt("y", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!getStringArg()) return false;									// Get the text
		aBundle.putString("text", StringConstant);
		if (!checkEOL()) return false;

		  int p = PaintList.size();												// Set the current paint	
		  aBundle.putInt("paint", p-1);											// as the text paint

		  NumericVarValues.set(SaveValueIndex, (double) DisplayList.size()); // Save the GR Object index into the var
		  DisplayListAdd(aBundle);

		return true;
	}

	private boolean execute_gr_text_align(){
		  Paint tPaint = newPaint(aPaint);										// Clone the current paint
		  if (!evalNumericExpression()) return false;							// Get Align parameter
			  double d = EvalNumericExpressionValue;
			  if (d == 1){ tPaint.setTextAlign(Paint.Align.LEFT);}              // Set the paint align value
			  else if (d == 2){ tPaint.setTextAlign(Paint.Align.CENTER);}
			  else if (d == 3){ tPaint.setTextAlign(Paint.Align.RIGHT);}
			  else {
				  RunTimeError( "Align value not 1, 2 or 3 at ");
				  return false;
			  }
				if (!checkEOL()) return false;
			  aPaint = newPaint(tPaint);				// Set the new current paint
		      PaintListAdd(aPaint);						// and add it to the paint list
			  return true;
	}

	private boolean execute_gr_text_size(){
			Paint tPaint = newPaint(aPaint);            // Clone the current paint
			if (!evalNumericExpression()) return false; // Get desired size
			double d = EvalNumericExpressionValue;
			if (!checkEOL()) return false;
			if (d < 1){
				RunTimeError( "must be > 0");
				return false;
			 }
			tPaint.setTextSize((int)d);					// Set the text size in paint
			 aPaint = newPaint(tPaint);				// Clone the temp paint into current paint
			 PaintListAdd(aPaint);						// Add current paint to the paint list
			 return true;
	}

	private boolean execute_gr_text_underline(){
			Paint tPaint = newPaint(aPaint);
			if (!evalNumericExpression()) return false;							// Get boolean
			if (!checkEOL()) return false;
			double d = EvalNumericExpressionValue;
			if (d==0) tPaint.setUnderlineText(false);
			else tPaint.setUnderlineText(true);
			
			 aPaint = newPaint(tPaint);
			 PaintListAdd(aPaint);
			 return true;
	}

	private boolean execute_gr_text_skew(){
			Paint tPaint = newPaint(aPaint);
			if (!evalNumericExpression()) return false;							// Get Skew
			double d = EvalNumericExpressionValue;
			if (!checkEOL()) return false;
			tPaint.setTextSkewX((float)d);
			 aPaint = newPaint(tPaint);
			 PaintListAdd(aPaint);
			 return true;
	}

	private boolean execute_gr_text_bold(){
			Paint tPaint = newPaint(aPaint);
			if (!evalNumericExpression()) return false;							// Get boolean 
			double d = EvalNumericExpressionValue;
			if (!checkEOL()) return false;
			int flag = tPaint.getFlags();
			if ( d==0) flag = flag & 0xdf;
			else flag = flag | 0x20;
			tPaint.setFlags(flag);

			aPaint = newPaint(tPaint);
			 PaintListAdd(aPaint);
			 return true;
	}

	private boolean execute_gr_text_strike(){
			Paint tPaint = newPaint(aPaint);
			if (!evalNumericExpression()) return false;							// Get boolean 
			double d = EvalNumericExpressionValue;
			if (!checkEOL()) return false;
			int flag = tPaint.getFlags();
			if ( d==0) flag = flag & 0xef;
			else flag = flag | 0x10;
			tPaint.setFlags(flag);

			aPaint = newPaint(tPaint);
			 PaintListAdd(aPaint);
			 return true;
	}

	private boolean execute_gr_text_width(){
		if (!getNVar()) return false;							// Width return  Variable
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!getStringArg()) return false;						// Get the string
		if (!checkEOL()) return false;

		   float w = aPaint.measureText(StringConstant);        // Get the strings width
		   NumericVarValues.set(SaveValueIndex, (double) w);    // Save the width into the var

		return true;
	}

	private boolean execute_gr_bitmap_load(){
		if (!getNVar()) return false;									// Graphic Bitmap Pointer Variable
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!getStringArg()) return false;								// Get the file path
		if (!checkEOL()) return false;

		String fileName = StringConstant;								// The filename as given by the user
		BufferedInputStream bis = null;									// Establish an input stream
		try { bis = Basic.getBufferedInputStream(Basic.DATA_DIR, fileName); }
		catch (Exception e) { return RunTimeError(e); }
		if (bis == null) { return RunTimeError("No bitmap found"); }	// Can this happen?

		System.gc();													// Garbage collect

		aBitmap = BitmapFactory.decodeStream(bis);				// Create bitmap from the input stream

		try { bis.close(); }
		catch (Exception e) { return RunTimeError(e); }

		if (aBitmap == null) return RunTimeError("Bitmap load failed at:");
		   
		   NumericVarValues.set(SaveValueIndex, (double) BitmapList.size()); // Save the GR Object index into the var
		   
		   BitmapList.add(aBitmap); // Add the new bit map to the bitmap list
		   
		   return true;
	}

	private boolean execute_gr_bitmap_delete(){
		   if (!evalNumericExpression()) return false; 
		   if (!checkEOL()) return false;
		   int q = EvalNumericExpressionValue.intValue();
		   if (q<1 | q >= BitmapList.size()){
			   RunTimeError("Invalid Bitmap Pointer");
			   return false;			   
		   }
		   Bitmap SrcBitMap = BitmapList.get(q);
		   if (SrcBitMap != null)
		      SrcBitMap.recycle();
		   BitmapList.set(q, null);
		   System.gc();
		  return true;
	}

	private boolean execute_gr_bitmap_scale(){

		if (!getNVar()) return false;							// Graphic Destination Bitmap Pointer Variable
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;				// Get Source Bitmap
		int q = EvalNumericExpressionValue.intValue();
		if (q < 1 | q >= BitmapList.size()) {
			return RunTimeError("Invalid Bitmap Pointer");
		}

		Bitmap SrcBitMap = BitmapList.get(q);
		if (SrcBitMap == null){
			return RunTimeError("Bitmap was deleted");
		}
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get Width
		int Width = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get Height
		int Height = EvalNumericExpressionValue.intValue();

		boolean parm = true;
		if (isNext(',')) {													// optional scale parameter
			if (!evalNumericExpression()) return false;
			if (EvalNumericExpressionValue == 0.0) parm = false;
		}
		if (!checkEOL()) return false;

		if (Width == 0 || Height == 0) {
			return RunTimeError("Width and Height must not be zero");
		}

		try { aBitmap = Bitmap.createScaledBitmap(SrcBitMap, Width, Height, parm); }
		catch (Exception e) { return RunTimeError(e); }

			System.gc();
			NumericVarValues.set(SaveValueIndex, (double) BitmapList.size()); // Save the GR Object index into the var
			System.gc();
			BitmapList.add(aBitmap);

		return true;
	}

	private boolean execute_gr_bitmap_size(){

		if (!getNVar()) return false;							// Graphic Source Bitmap Pointer Variable
		int q = NumericVarValues.get(theValueIndex).intValue();
		if (q < 1 | q >= BitmapList.size()) {
			return RunTimeError("Invalid Bitmap Pointer");
		}

		Bitmap SrcBitMap = BitmapList.get(q);					// Access the bitmap
		if (SrcBitMap == null) {
			return RunTimeError("Bitmap was deleted");
		}

		int w = SrcBitMap.getWidth();							// Get the image width
		int h = SrcBitMap.getHeight();							// Get the image height

		if (!isNext(',')) return false;
		if (!getNVar()) return false;							// Get the height variable
		NumericVarValues.set(theValueIndex, (double) w);		// Set the height value

		if (!isNext(',')) return false;
		if (!getNVar()) return false;							// Get the width variable
		NumericVarValues.set(theValueIndex, (double) h);		// Set the width value
		if (!checkEOL()) return false;

		return true;
	}

	private boolean execute_gr_bitmap_crop(){
		if (!getNVar()) return false;							// Graphic Object Variable
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;				// Get source bitmap index
		int SourceBitmapIndex = EvalNumericExpressionValue.intValue();
		if (SourceBitmapIndex < 0 || SourceBitmapIndex >= BitmapList.size()) {
			return RunTimeError("Invalid Source Bitmap Pointer");
		}
		Bitmap SourceBitmap = BitmapList.get(SourceBitmapIndex);

		if (!isNext(',')) return false;
		if (!evalNumericExpression()) return false;							// Get x
		int x = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get y
		int y = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get width
		int width = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get height
		int height = EvalNumericExpressionValue.intValue();
		if (!checkEOL()) return false;

		  try {
			  aBitmap = Bitmap.createBitmap(SourceBitmap, x, y, width, height);
		  }
		  catch (Exception e){
			  return RunTimeError(e);
		  }

		   NumericVarValues.set(SaveValueIndex, (double) BitmapList.size()); // Save the GR Object index into the var
		   BitmapList.add(aBitmap);

		return true;
	}

	private boolean execute_gr_bitmap_draw(){
		  Bundle aBundle = new Bundle();         // Create a new display object of type bitmap
		  aBundle.putInt("type", GR.dBitmap);
		  aBundle.putInt("hide", 0);

		if (!getNVar()) return false;							// Graphic Object Variable
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;				// Get Bitmap obj pointer
		int q = EvalNumericExpressionValue.intValue();
		if (q < 1 | q >= BitmapList.size()) {
			return RunTimeError("Invalid Bitmap Pointer");
		}

		if (BitmapList.get(q) == null) {
			return RunTimeError("Bitmap was deleted");
		}
		aBundle.putInt("bitmap", q);
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get x
		aBundle.putInt("x", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get y
		aBundle.putInt("y", EvalNumericExpressionValue.intValue());
		if (!checkEOL()) return false;

		  int p = PaintList.size();						// Set current paint as this circle's paint
		  aBundle.putInt("paint", p-1);
		  
		  aBundle.putInt("alpha", 255);

		  NumericVarValues.set(SaveValueIndex, (double) DisplayList.size()); // Save the GR Object index into the var
		  DisplayListAdd(aBundle);

		return true;
	}

	private boolean execute_gr_bitmap_create(){
		if (!getNVar()) return false;										// Get bitmap ptr var
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get the width
		int width = EvalNumericExpressionValue.intValue();
		if (width <= 0) {
			return RunTimeError("Width must be >= 0");
		}
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get the height
		int height = EvalNumericExpressionValue.intValue();
		if (height <= 0) {
			return RunTimeError("Height must be >= 0");
		}
		if (!checkEOL()) return false;

		   try{
			   aBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); // Create the bitamp
		   }
		   catch (Exception e) {
			   return RunTimeError(e);
		   }
		   
		   NumericVarValues.set(SaveValueIndex, (double) BitmapList.size()); // Save the GR Object index into the var
		   BitmapList.add(aBitmap);
		   		  
		return true;
	}

	private boolean execute_gr_rotate_start(){

		  Bundle aBundle = new Bundle();            // Create a new display list object of type rotate
		  aBundle.putInt("type", GR.dRotate_Start);
		  aBundle.putInt("hide", 0);

		if (!evalNumericExpression()) return false;							// Get angle
		aBundle.putInt("angle", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get x
		aBundle.putInt("x", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get y
		aBundle.putInt("y", EvalNumericExpressionValue.intValue());

		if (isNext(',')) {
			if (!getNVar()) return false;
			NumericVarValues.set(theValueIndex, (double) DisplayList.size()); // Save the GR Object index into the var
		}
		if (!checkEOL()) return false;

		DisplayListAdd(aBundle);					// Put the new object into the display list

		return true;
	}

	private boolean execute_gr_rotate_end(){
		  Bundle aBundle = new Bundle();           // Create a new object of type Rotate end
		  aBundle.putInt("type", GR.dRotate_End); 
		  aBundle.putInt("hide", 0);

		if (!isEOL()) {
			if (!getNVar()) return false; 
			NumericVarValues.set(theValueIndex, (double) DisplayList.size()); // Save the GR Object index into the var
			if (!checkEOL()) return false;
		}

		  DisplayListAdd(aBundle);					// add the object to the display list
		  return true;
	}

	private boolean execute_gr_modify(){

		if (!evalNumericExpression()) return false;							// Get Object Number
		int index = EvalNumericExpressionValue.intValue();
		if (index < 0 || index >= DisplayList.size()) {
			return RunTimeError("Object Number out of range");
		}

		if (!isNext(',')) return false;
		if (!getStringArg()) return false;									// get the parameter string
		if (!isNext(',')) return false;
		String parm = StringConstant;

		Bundle b = DisplayList.get(index);									// Get the bundle to change
		if (!b.containsKey(parm)) {
			return RunTimeError("Object does not contain: " + parm);
		}

		if (parm.equals("text")) {
			if (!getStringArg()) return false;								// get the parameter string
			if (!checkEOL()) return false;
			b.putString(parm, StringConstant);
		} else {
			if (!evalNumericExpression()) return false;						// Get parameter value
			if (!checkEOL()) return false;
			int value = EvalNumericExpressionValue.intValue();
			if (parm.equals("bitmap")) {
				if (value < 0 | value >= BitmapList.size()) {
					return RunTimeError("Bitmap pointer out of range");
				}
			} else if (parm.equals("paint")) {
				if (value < 1 || value >= PaintList.size()) {
					return RunTimeError ("Invalid Paint object number");
				}
			}
			b.putInt(parm, value);
		}

		DisplayList.set(index, b);
		return true;
	}

	private boolean execute_gr_orientation(){
		if (!evalNumericExpression()) return false;		// get the mode (landscape or portrait)
		if (!checkEOL()) return false;
		int mode = EvalNumericExpressionValue.intValue();
		GR.drawView.SetOrientation(mode);
		return true;
	}

	  private boolean execute_gr_screen(){
		   if (!getNVar()) return false;						// Width variable
		   NumericVarValues.set(theValueIndex,(double) GR.Width); 
		   if (!isNext(',')) return false;

		   if (!getNVar()) return false;						// Heigth Variable
		   NumericVarValues.set(theValueIndex, (double) GR.Heigth); 
		   if (isNext(',')) {
			   if (!getNVar()) return false;					// Optional Density variable
			   DisplayMetrics dm = new DisplayMetrics();
			   getWindowManager().getDefaultDisplay().getMetrics(dm);
			   NumericVarValues.set(theValueIndex, (double) dm.densityDpi);
		   }
		   if (!checkEOL()) return false;

		  return true;
	  }

	private boolean execute_gr_front(){
		if (!evalNumericExpression()) return false;						// Get flag
		if (EvalNumericExpressionValue == 0) {
			  Basic.theProgramRunner.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT );
			  startActivity(Basic.theProgramRunner);
			  GRFront = false;
		} else {
			  GRclass.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
			  startActivity(GRclass);
			  GRFront = true;
		}
		if (!checkEOL()) return false;
		try {Thread.sleep(100);}catch(InterruptedException e){}
		return true;
	}

	private boolean execute_gr_set_pixels(){

		if (!getNVar()) return false;							// Graphic Object Variable
		int SaveValueIndex = theValueIndex;

		if (!isNext(',')) return false;
		if (getArrayVarForRead() == null) return false;			// Get the array variable
		if (!VarIsNumeric) { return RunTimeError(EXPECT_NUM_ARRAY); }
		int arrayTableIndex = VarIndex.get(VarNumber);

		Integer[] pair = { null, null };
		if (!getIndexPair(pair)) return false;					// Get values inside [], if any

		int x = 0;
		int y = 0;
		if (isNext(',')) {
			if (!evalNumericExpression()) return false;
			x = EvalNumericExpressionValue.intValue();
			if (!isNext(',')) return false;
			if (!evalNumericExpression()) return false;
			y = EvalNumericExpressionValue.intValue();
		}
		if (!checkEOL()) return false;

		if (!getArraySegment(arrayTableIndex, pair)) { return false; }	// Get array base and length
		int base = pair[0].intValue();
		int length = pair[1].intValue();
		if ((length % 2) != 0){
			return RunTimeError("Not an even number of elements in pixel array");
		}

/*		int pixelBase = PixelPoints.size();
		for (int i = 0; i < length; ++i){
			double d = NumericVarValues.get(base+i);
			PixelPoints.add((float) d);
		}
*/
		Bundle aBundle = new Bundle();
		aBundle.putInt("type", GR.dsetPixels);
		aBundle.putInt("hide", 0);
		aBundle.putInt("pbase", base);
		aBundle.putInt("plength", length);
		aBundle.putInt("x", x);
		aBundle.putInt("y", y);

		int p = PaintList.size();								// Set the most current paint as the paint
		aBundle.putInt("paint", p-1);							// paint for these pixels

		NumericVarValues.set(SaveValueIndex, (double) DisplayList.size()); // Save the GR Object index into the var
		DisplayListAdd(aBundle);								// Add the line to the display list

		return true;
	}

	  private boolean execute_gr_get_bmpixel(){
		  if (!evalNumericExpression()) return false;
		  int SourceBitmapIndex = EvalNumericExpressionValue.intValue();
		  if (!isNext(',')) { return false; }
		  
		   if (SourceBitmapIndex < 0 || SourceBitmapIndex >= BitmapList.size()){
			   RunTimeError("Invalid Source Bitmap Pointer");
			   return false;
		   }
		   Bitmap SourceBitmap = BitmapList.get(SourceBitmapIndex);
		   
		   return getTheBMpixel(SourceBitmap);
	  }
	  
	  private boolean execute_gr_get_pixel(){
	  		
            boolean retval = true;
			Bitmap b = getTheBitmap();									// get the DrawingCache bitmap
			if (b == null){
				RunTimeError("Could not capture screen bitmap. Sorry.");
				retval = false;
			} else {

				retval = getTheBMpixel(b);								// get the requested pixel

				b.recycle();											// clean up bitmap
				b = null;
			}
			GR.drawView.destroyDrawingCache();							// clean up DrawingCache
			System.gc();
			return retval;
	  }

		// After it's done with the Bitmap, caller should call destroyDrawingCache()
		private Bitmap getTheBitmap() {
			synchronized (GR.Rendering) {
				GR.drawView.setDrawingCacheEnabled(true);
				GR.Rendering = true;									// buildDrawingCache() renders
				GR.drawView.buildDrawingCache();						// Build the cache
				return GR.drawView.getDrawingCache();					// get the bitmap
			}
		}

	private boolean getTheBMpixel(Bitmap b){

		if (!evalNumericExpression()) return false;						// Get x
		int x = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;						// Get y
		int y = EvalNumericExpressionValue.intValue();
		if (!isNext(',')) return false;

		int w = b.getWidth();											// Get the image width
		int h = b.getHeight();											// Get the image height
		if (x < 0 || x >= w || y < 0 || y >= h) {
			return RunTimeError("x or y exceeds size of screen");
		}

		int pixel = (b == null) ? 0 : b.getPixel(x, y);					// get the pixel from the bitmap

		int alpha = Color.alpha(pixel);									// get the components of the pixel
		int red = Color.red(pixel);
		int green = Color.green(pixel);
		int blue = Color.blue(pixel);

		if (!getNVar()) return false;									// Alpha Var
		NumericVarValues.set(theValueIndex, (double) alpha);
		if (!isNext(',')) return false;

		if (!getNVar()) return false;									// Red Var
		NumericVarValues.set(theValueIndex, (double) red);
		if (!isNext(',')) return false;

		if (!getNVar()) return false;									// Blue Green
		NumericVarValues.set(theValueIndex, (double) green);
		if (!isNext(',')) return false;

		if (!getNVar()) return false;									// Green Blue
		NumericVarValues.set(theValueIndex, (double) blue);
		if (!checkEOL()) return false;

		return true;
	}

	private boolean writeBitmapToFile(Bitmap b, String fn, int quality) {
		CompressFormat format = CompressFormat.PNG;						// Assume png
		String tFN = fn.toUpperCase();									// temp convert fn to upper case
		if (tFN.endsWith(".JPG")) format = CompressFormat.JPEG;			// Test jpg
		else if (!tFN.endsWith(".PNG")) fn = fn + ".png";				// Test png

		File file = new File(Basic.getDataPath(fn));					// build full path
		FileOutputStream ostream = null;

		try {															// Write the file
			file.createNewFile();
			ostream = new FileOutputStream(file);

			b.compress(format, quality, ostream);						// Write png or jpg
			ostream.close();
		} 
		catch (Exception e) {
			closeStream(ostream, null);
			return RunTimeError(e);
		}
		return true;
	}

	private boolean execute_gr_save(){

		if (!getStringArg()) return false;							// Get the filename
		String fn = StringConstant;

			int quality = 50;											// set default jpeg quality
			if (isNext(','))											// if there is an optional quality parm
			{
				if (!evalNumericExpression()) return false;				// evaluate it
				quality = EvalNumericExpressionValue.intValue();
				if (quality < 0 || quality > 100) {
					return RunTimeError("Quality must be between 0 and 100");
				}
			}
			if (!checkEOL()) return false;

			boolean retval = true;
			Bitmap b = getTheBitmap();									// get the DrawingCache bitmap
			if (b == null) {
				RunTimeError("Problem creating bitmap");
				retval = false;
			} else {

				retval = writeBitmapToFile(b, fn, quality);

				b.recycle();											// clean up bitmap
				b = null;
			}
			GR.drawView.destroyDrawingCache();							// clean up DrawingCache
			System.gc();
			return retval;
	}

	private boolean  execute_screen_to_bitmap(){
		if (!getNVar()) return false;
		if (!checkEOL()) return false;
		boolean retval = true;
		Bitmap b = getTheBitmap();									// get the DrawingCache bitmap
		if (b == null) {
			return RunTimeError("Could not capture screen bitmap. Sorry.");
		} else {

			  NumericVarValues.set(theValueIndex, (double) BitmapList.size()); // Save the GR Object index into the var
			  BitmapList.add(b.copy(Bitmap.Config.ARGB_8888 , true));			
			  retval = true;

			  b.recycle();												// clean up bitmap
			  b = null;
		  }
		  GR.drawView.destroyDrawingCache();							// clean up DrawingCache
		  System.gc();
		  return retval;
	}

	private boolean execute_gr_get_texbounds(){
		if (!getStringArg()) return false;
		String text = StringConstant;

		if (!isNext(',')) return false;
		if (!getNVar()) return false;
		int leftIndex = theValueIndex;

		if (!isNext(',')) return false;
		if (!getNVar()) return false;
		int topIndex = theValueIndex;

		if (!isNext(',')) return false;
		if (!getNVar()) return false;
		int rightIndex = theValueIndex;

		if (!isNext(',')) return false;
		if (!getNVar()) return false;
		int bottomIndex = theValueIndex;
		if (!checkEOL()) return false;

		  Rect bounds = new Rect();
		  
		  aPaint.getTextBounds(text, 0, text.length(), bounds);
		  
		  int left = bounds.left;
		  int top = bounds.top;
		  int right = bounds.right;
		  int bottom = bounds.bottom;
		  
		  NumericVarValues.set(topIndex, (double) top);
		  NumericVarValues.set(leftIndex, (double) left);
		  NumericVarValues.set(bottomIndex, (double) bottom);
		  NumericVarValues.set(rightIndex, (double) right);
		  
		  return true;
	}

	private boolean execute_bitmap_save(){

		if (!evalNumericExpression()) return false;			// Get Source Bitmap
		int q = EvalNumericExpressionValue.intValue();
		if (q < 1 | q >= BitmapList.size()) {
			return RunTimeError("Invalid Bitmap Pointer");
		}

		Bitmap SrcBitMap = BitmapList.get(q);
		if (SrcBitMap == null) {
			return RunTimeError("Bitmap was deleted");
		}
		if (!isNext(',')) return false;

		if (!getStringArg()) return false;					// Get the filename
		String fn = StringConstant;

		int quality = 50;									// set default jpeg quality
		if (isNext(',')) {									// if there is an optional quality parm
			if (!evalNumericExpression()) return false;		// evaluate it
			quality = EvalNumericExpressionValue.intValue();
			if (quality < 0 || quality > 100) {
				return RunTimeError("Quality must be between 0 and 100");
			}
		}
		if (!checkEOL()) return false;

			boolean retval = writeBitmapToFile(SrcBitMap, fn, quality);

		    SrcBitMap = null;
		    System.gc();
			return retval;
	}

	private boolean execute_gr_scale(){

		if (!evalNumericExpression()) return false;								// Get x
		double x = EvalNumericExpressionValue;

		if (!isNext(',')) return false;
		if (!evalNumericExpression()) return false;								// Get y
		double y = EvalNumericExpressionValue;
		if (!checkEOL()) return false;

		GR.scaleX = (float) x;
		GR.scaleY = (float) y;

		return true;
	}

	private boolean execute_gr_clip(){

		  Bundle aBundle = new Bundle();
		  aBundle.putInt("type", GR.dClip);
		  aBundle.putInt("hide", 0);

		if (!getNVar()) return false;							// Graphic Object Variable
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get left
		aBundle.putInt("left", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get top
		aBundle.putInt("top", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get right
		aBundle.putInt("right", EvalNumericExpressionValue.intValue());
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;							// Get bottom
		aBundle.putInt("bottom", EvalNumericExpressionValue.intValue());

		if (isNext(',')) {
			if (!evalNumericExpression()) return false;
			int RegionOp = EvalNumericExpressionValue.intValue();
			if (RegionOp < 0 || RegionOp > 5) {
				return RunTimeError("Region Operator not 0 to 5");
			}
			aBundle.putInt("RO", RegionOp);
		}
		if (!checkEOL()) return false;

		  int p = PaintList.size();						// Set current paint as this circle's paint
		  aBundle.putInt("paint", p-1);
		  NumericVarValues.set(SaveValueIndex, (double) DisplayList.size()); 	// Save the GR Object index into the var

		  DisplayListAdd(aBundle);
		  return true;
	}

	private boolean execute_gr_poly(){
		  Bundle aBundle = new Bundle();
		  aBundle.putInt("type", GR.dPoly);
		  aBundle.putInt("hide", 0);

		if (!getNVar()) return false;							// Graphic Object Variable
		int SaveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;
		int theListIndex = EvalNumericExpressionValue.intValue();
		if (theListIndex < 1 || theListIndex >= theLists.size()) {
			return RunTimeError("Invalid list pointer");
		}
		if (theListsType.get(theListIndex) != list_is_numeric){
			return RunTimeError("List must be numeric");
		}

		ArrayList<Double> thisList = theLists.get(theListIndex);
		if (thisList.size() < 6) {
			return RunTimeError("List must have at least three points");
		}

		int r = thisList.size() % 2;
		if (r != 0) {
			return RunTimeError("List must have even number of elements");
		}
		aBundle.putInt("list", theListIndex);

		int x = 0;
		int y = 0;
		if (isNext(',')) {
			if (!evalNumericExpression()) return false;
			x = EvalNumericExpressionValue.intValue();
			if (!isNext(',')) return false;
			if (!evalNumericExpression()) return false;
			y = EvalNumericExpressionValue.intValue();
		}
		if (!checkEOL()) return false;

	  		aBundle.putInt("x", x);
	  		aBundle.putInt("y", y);


		   int p = PaintList.size();						// Set current paint as this circle's paint
		   aBundle.putInt("paint", p-1);
		   NumericVarValues.set(SaveValueIndex, (double) DisplayList.size()); 	// Save the GR Object index into the var

		   DisplayListAdd(aBundle);

		  return true;
	}

	@SuppressLint("NewApi")										// Uses value from API 9
	private static int getNumberOfCameras() {

			int cameraCount;
			int level = Build.VERSION.SDK_INT;
			if (level < 9) {											// if SDK < 9 there can be only one camera
				Camera tCamera = Camera.open();							// Check to see if there is any camera at all
				cameraCount = (tCamera == null) ? 0 : 1;
				tCamera.release();
			} else {
				cameraCount = Camera.getNumberOfCameras();				// May be more than one camera
			}
			return cameraCount;
	}

	@SuppressLint("NewApi")										// Uses value from API 9
	private boolean execute_gr_camera_select(){

		if (NumberOfCameras < 0) { NumberOfCameras = getNumberOfCameras(); }
		if (NumberOfCameras == 0) { return RunTimeError("This device does not have a camera."); }

		int level = Build.VERSION.SDK_INT;
		if (level < 9) {											// if SDK < 9 there can be only one camera
			CameraNumber = -1;										// so no selection is possible
			return (NumberOfCameras != 0);
		}

		int BACK = 1;
		int FRONT = 2;

		if (!evalNumericExpression()) { return false; }				// Get user's choice
		if (!checkEOL()) { return false; }

		int choice = EvalNumericExpressionValue.intValue();
		if (choice != BACK && choice != FRONT) {
			return RunTimeError("Select value must be 1 (Back) or 2 (Front).");
		}

		Camera.CameraInfo CI = new Camera.CameraInfo();				// Determine which camera number is BACK
		Camera.getCameraInfo(0, CI);								// Assume 0 is BACK
		boolean zero_is_back = (CI.facing == CameraInfo.CAMERA_FACING_BACK);

		if (NumberOfCameras == 1) {									// Camera 0 is the only camera
			CameraNumber = 0;
			if ((choice == BACK) && !zero_is_back) { return RunTimeError("Device has no back camera"); }
			if ((choice == FRONT) && zero_is_back) { return RunTimeError("Device has no front camera"); }
		} else {
			CameraNumber = ((choice == BACK) == zero_is_back) ? 0 : 1;
		}
		return true;
	}

	private boolean execute_camera_shoot(int pictureMode){

		if (NumberOfCameras < 0) { NumberOfCameras = getNumberOfCameras(); }
		if (NumberOfCameras == 0) { return RunTimeError("This device does not have a camera."); }

		int flashMode = 0;
		int focusMode = 0;
		if (!getNVar()) return false;
		int saveValueIndex = theValueIndex;

		if (isNext(',')) {
			if (!evalNumericExpression()) { return false; }
			flashMode = EvalNumericExpressionValue.intValue();

			if (isNext(',')) {											// Focus/2013-07-25 gt
				if (!evalNumericExpression()) { return false; }
				focusMode = EvalNumericExpressionValue.intValue();
			}
		}
		if (!checkEOL()) { return false; };

		CameraBitmap = null;
		CameraDone = false;
		Intent cameraIntent = new Intent(this, CameraView.class);		// Start the Camera
		cameraIntent.putExtra(CameraView.EXTRA_PICTURE_MODE, pictureMode);
		cameraIntent.putExtra(CameraView.EXTRA_CAMERA_NUMBER, CameraNumber);
		cameraIntent.putExtra(CameraView.EXTRA_FLASH_MODE, flashMode);
		cameraIntent.putExtra(CameraView.EXTRA_FOCUS_MODE, focusMode);
		try {
			startActivityForResult(cameraIntent, BASIC_GENERAL_INTENT);
		} catch (Exception e) {
			return RunTimeError(e);
		}
		while (!CameraDone) Thread.yield();

			if (CameraBitmap != null) {
			   NumericVarValues.set(saveValueIndex, (double) BitmapList.size()); // Save the GR Object index into the var
			   BitmapList.add(CameraBitmap);
			} else {
			   NumericVarValues.set(saveValueIndex, (double) 0); // Save the GR Object index into the var
			}
			CameraBitmap = null;
		    System.gc();

			return true;
	}

	private boolean execute_statusbar_show(){
		String[] msg = {
			"This command deprecated.",							// First line is base of errorMsg
			"To show status bar, use:",
			"gr.open alpha, red, green, blue, 1"
		};
		return RunTimeError(msg);
	}

	private boolean execute_brightness(){
		if (!evalNumericExpression()) return false;
		if (!checkEOL()) return false;
		double value = EvalNumericExpressionValue;
		if (value < 0.01) value = 0.01;
		if (value > 1.0) value = 1.0;
		GR.Brightness = (float) value;
		return true;
	}

	private boolean execute_gr_text_typeface(){
		if (!evalNumericExpression()) return false;							// Get type
		if (!checkEOL()) { return false; }
		int face = EvalNumericExpressionValue.intValue();

			int style = Typeface.NORMAL;	

			Typeface tf ;														// Interpret typeface
			if (face == 1)
				tf = Typeface.create(Typeface.DEFAULT, style);
			else if (face == 2)
				tf = Typeface.create(Typeface.MONOSPACE, style);
			else if (face == 3)
				tf = Typeface.create(Typeface.SANS_SERIF, style);
			else if (face == 4)
				tf = Typeface.create(Typeface.SERIF, style);
			else {
				return RunTimeError("Typface must be 1, 2, 3 or 4");
			}
			
			Paint tPaint = newPaint(aPaint);						// Put the typeface into Paint
			tPaint.setTypeface(tf);
			aPaint =  newPaint(tPaint);							// Copy the temp paint to aPaint
		    PaintListAdd(aPaint);								// Add the newPaint to the Paint List
			
		  return true;
	}

	private boolean execute_gr_touch_resume(){
		if (interruptResume == -1) {
			return RunTimeError("No onTouch Interrupt");
		}
		return doResume();
	}

//*************************************************************  Audio  ******************************************
	  private boolean executeAUDIO(){
	    	if (!GetAudioKeyWord()){
  	    	  return false;
  	      	}else {
  	    	  switch (KeyWordValue){
  	    	  	case audio_load:
  	    	  		if (!execute_audio_load()){return false;}
  	    	  		break;
  	    	  	case audio_play:
  	    	  		if (!execute_audio_play()){return false;}
  	    	  		break;
  	    	  	case audio_loop:
  	    	  		if (!execute_audio_loop()){return false;}
  	    	  		break;
  	    	  	case audio_stop:
  	    	  		if (!execute_audio_stop()){return false;}
  	    	  		break;
  	    	  	case audio_volume:
  	    	  		if (!execute_audio_volume()){return false;}
  	    	  		break;
  	    	  	case audio_pcurrent:
  	    	  		if (!execute_audio_pcurrent()){return false;}
  	    	  		break;
  	    	  	case audio_pseek:
  	    	  		if (!execute_audio_pseek()){return false;}
  	    	  		break;
  	    	  	case audio_length:
  	    	  		if (!execute_audio_length()){return false;}
  	    	  		break;
  	    	  	case audio_release:
  	    	  		if (!execute_audio_release()){return false;}
  	    	  		break;
  	    	  	case audio_pause:
  	    	  		if (!execute_audio_pause()){return false;}
  	    	  		break;
  	    	  	case audio_isdone:
  	    	  		if (!execute_audio_isdone()){return false;}
  	    	  		break;
  	    	  	case audio_record_start:
  	    	  		if (!execute_audio_record_start()){return false;}
  	    	  		break;
  	    	  	case audio_record_stop:
  	    	  		if (!execute_audio_record_stop()){return false;}
  	    	  		break;
  	    	  	default:
  	    	  		return false;
  	    	  }
  	      	}
		  return true;
	  }

	private boolean GetAudioKeyWord(){						// Get an audio command keyword if it is there
															// is the current line index at a keyword?
		for (int i = 0; i < Audio_KW.length; ++i) {			// loop through the keyword list
			if (ExecutingLineBuffer.startsWith(Audio_KW[i], LineIndex)) { // if there is a match
				KeyWordValue = i;							// set the keyword number
				LineIndex += Audio_KW[i].length();			// move the line index to end of keyword
				return true;								// and report back
			}
		}
		KeyWordValue = audio_none;							// no keyword found
		return false;										// report fail
	}

	private MediaPlayer getMP(String fileName) {
		MediaPlayer mp = null;
		File file = new File(Basic.getDataPath(fileName));
		if (file.exists()) {
			Uri uri = Uri.fromFile(file);								// Create Uri for the file
			if (uri != null) {
				mp = MediaPlayer.create(Basic.BasicContext, uri);		// Create a new Media Player
			}
		} else {														// the file does not exist
			if (Basic.isAPK) {											// we are in APK
				int resID = Basic.getRawResourceID(fileName);			// try to load the file from a raw resource
				if (resID != 0) {
					mp = MediaPlayer.create(Basic.BasicContext, resID);
				} else {												// try to load the file from assets
					AssetFileDescriptor afd = null;
					try {
						String assetPath = Basic.getAppFilePath(Basic.DATA_DIR, fileName);
						afd = getAssets().openFd(assetPath);
						mp = new MediaPlayer();
						mp.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength());
						mp.prepare();
					} catch (IOException e) {
						if (mp != null) {
							mp.release();
							mp = null;
						}
					} finally {
						if (afd != null) { try { afd.close(); } catch (IOException e) { } }
					}
				}
			}
		}
		return mp;
	}

	private boolean execute_audio_load(){
		  /* If there is already an audio running,
		   * then stop it and 
		   * release its resources.
		   */

/*		  if (theMP != null){
			  try {theMP.stop();} catch (IllegalStateException e){}
			  theMP.release();
			  theMP = null;
		  }*/
		setVolumeControlStream(AudioManager.STREAM_MUSIC);
//		AudioManager audioSM = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);

		if (!getNVar()) return false;									// Get the Player Number Var
		int saveValueIndex = theValueIndex;
		if (!isNext(',')) return false;

		if (!getStringArg()) return false;								// Get the file path
		if (!checkEOL()) return false;

		String fileName = StringConstant;								// The filename as given by the user
		MediaPlayer aMP = getMP(fileName);

		if (aMP == null) { return RunTimeError(fileName + " Not Found at:"); }
		aMP.setAudioStreamType(AudioManager.STREAM_MUSIC);
		setVolumeControlStream(AudioManager.STREAM_MUSIC);

		NumericVarValues.set(saveValueIndex, (double)theMPList.size());
		theMPList.add(aMP);
		theMPNameList.add(fileName);
		return true;
	}

	private boolean execute_audio_release(){

		if (!evalNumericExpression()) return false;
		int index = EvalNumericExpressionValue.intValue();
		if (index <= 0 || index >= theMPList.size()) {
			return RunTimeError("Invalid Player List Value");
		}
		if (!checkEOL()) return false;

		MediaPlayer aMP = theMPList.get(index);
		if (aMP == null) return true;

		if (theMP == aMP) { return RunTimeError("Must stop player before releasing"); }

		aMP.release();
		theMPList.set(index, null);

		return true;
	}

	private boolean execute_audio_play(){

		if (!evalNumericExpression()) return false;
		int index = EvalNumericExpressionValue.intValue();
		if (index <= 0 || index >= theMPList.size()) {
			return RunTimeError("Invalid Player List Value");
		}
		if (!checkEOL()) return false;

		MediaPlayer aMP = theMPList.get(index);
		if (aMP == null) { return RunTimeError("Audio not loaded at:"); }
		if (theMP != null) { return RunTimeError("Stop Current Audio Before Starting New Audio"); }

		setVolumeControlStream(AudioManager.STREAM_MUSIC);
//		Log.v(LOGTAG, CLASSTAG + " play " + aMP);

		try { aMP.prepare(); } catch (Exception e) { }
		aMP.start();

		if (!aMP.isPlaying()) {								// Somehow lost the player. Make a new one.
			aMP.release();
			aMP = getMP(theMPNameList.get(index));
			if (aMP == null) {
				RunTimeError("Media player synchronous problem.");
				return true;								// Is this correct?
			}
			theMPList.set(index, aMP);
			aMP.start();
		}

		aMP.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
			public void onCompletion(MediaPlayer mp) {
				PlayIsDone = true;
			}
		});
//		Log.v(LOGTAG, CLASSTAG + " is playing " + theMP.isPlaying());
		PlayIsDone = false;
		theMP = aMP;

		return true;
	}

	private boolean execute_audio_isdone(){
		if (!getNVar()) return false;	
		if (!checkEOL()) return false;

		if (theMP == null) {
			PlayIsDone = true;
		}
		double d = (PlayIsDone) ? 1 : 0;
		NumericVarValues.set(theValueIndex, d);

		return true;
	}

	private boolean execute_audio_loop(){
		if (theMP == null) {
			return RunTimeError("Audio not playing at:");
		}
		if (!checkEOL()) return false;

		theMP.setLooping(true);
		return true;
	}

	private boolean execute_audio_stop(){
		if (!checkEOL()) return false;
		if (theMP == null) return true;								// if theMP is null, Media player has stopped

//		MediaPlayer.setOnSeekCompleteListener(mSeekListener);
//		try {Thread.sleep(1000);}catch(InterruptedException e){}
		try { theMP.stop(); }
		catch (Exception e) { return RunTimeError(e); }
		finally { theMP = null; }									// Signal MP stopped

		return true;
	}

	private boolean execute_audio_pause(){
		if (!checkEOL()) return false;
		if (theMP == null) return true;								// if theMP is null, Media player has stopped

		try { theMP.pause(); }
		catch (Exception e) { return RunTimeError(e); }
		finally { theMP = null; }									// Signal MP stopped

		return true;
	}

	private boolean execute_audio_volume(){
		if (theMP == null) {
			return RunTimeError("Audio not playing at:");
		}
		if (!evalNumericExpression()) return false;
		float left = EvalNumericExpressionValue.floatValue();

		if (!isNext(',')) return false;
		if (!evalNumericExpression()) return false;
		float right = EvalNumericExpressionValue.floatValue();
		if (!checkEOL()) return false;

		setVolumeControlStream(AudioManager.STREAM_MUSIC);
		theMP.setVolume(left, right);

		return true;
	}

	private boolean execute_audio_pcurrent(){
		if (theMP == null) {
			return RunTimeError("Audio not playing at:");
		}
		if (!getNVar()) return false;
		if (!checkEOL()) return false;

		NumericVarValues.set(theValueIndex, (double)theMP.getCurrentPosition());

		return true;
	}

	private boolean execute_audio_pseek(){
		if (theMP == null) {
			return RunTimeError("Audio not playing at:");
		}
		if (!evalNumericExpression()) return false;
		if (!checkEOL()) return false;

		int pos = EvalNumericExpressionValue.intValue();
		theMP.seekTo(pos);

		return true;
	}

	private boolean execute_audio_length(){
		if (!getNVar()) return false;								// Get the Player Number Var
		int saveValueIndex = theValueIndex;

		if (!isNext(',')) return false;
		if (!evalNumericExpression()) return false;
		int index = EvalNumericExpressionValue.intValue();
		if (index <= 0 || index >= theMPList.size()) {
			return RunTimeError("Invalid Player List Value");
		}
		if (!checkEOL()) return false;

		MediaPlayer aMP = theMPList.get(index);
		if (aMP == null) {
			return RunTimeError("Audio not loaded at:");
		}

		int length = aMP.getDuration();
		NumericVarValues.set(saveValueIndex, (double)length);

		return true;
	}

	private boolean execute_audio_record_start(){
		if (!getStringArg()) return false;
		String recordFileName = Basic.getDataPath(StringConstant);

		int source = 0;									// Get optional source
		if (isNext(',')) {
			if (!evalNumericExpression()) return false;
			source = EvalNumericExpressionValue.intValue();
		}
		if (!checkEOL()) return false;

		  try {
	        mRecorder = new MediaRecorder();
	        switch (source)
	        {
	        case 0:
	        	mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
	        	break;
	        case 1:
	        	mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);
	        	break;
	        case 2:
	        	mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);
	        	break;
	        case 3:
	        	mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_DOWNLINK);
	        	break;
	        case 4:
	        	mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_UPLINK);
	        	break;
	        }
	        	
	        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
	        mRecorder.setOutputFile(recordFileName);
	        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
	        	        
	        mRecorder.prepare();
		    mRecorder.start();

	        } catch (Exception e) {
	            RunTimeError("Audio record error: " + e);
	            return false;
	        }

	        
	        return true;

	  }
	  
	  private boolean execute_audio_record_stop(){
		    if (mRecorder == null) return true;
		    try {
		    	mRecorder.stop();
		    	mRecorder.release();
		    }
		    catch (Exception e){}
	        mRecorder = null;

		  return true;
	  }

	  
// *********************************************  Sensors Package **********************************
	  
	  private boolean executeSENSORS(){
	    	if (!GetSensorsKeyWord()){
	    	  return false;
	      	}else {
	    	  switch (KeyWordValue){
	    	  	case sensors_list:
	    	  		if (!execute_sensors_list()){return false;}
	    	  		break;
	    	  	case sensors_open:
	    	  		if (!execute_sensors_open()){return false;}
	    	  		break;
	    	  	case sensors_read:
	    	  		if (!execute_sensors_read()){return false;}
	    	  		break;
	    	  	case sensors_close:
	    	  		if (!execute_sensors_close()){return false;}
	    	  		break;
	    	  	case sensors_rotate:
	    	  		if (!execute_sensors_rotate()){return false;}
	    	  		break;
	    	  	default:
	    	  		return false;
	    	  }
	      	}
		  return true;
	  }

	private boolean GetSensorsKeyWord(){					// Get a sensors commmand keyword if it is there
															// is the current line index at a keyword?
		for (int i = 0; i < Sensors_KW.length; ++i) {		// loop through the keyword list
			if (ExecutingLineBuffer.startsWith(Sensors_KW[i], LineIndex)) { // if there is a match
				KeyWordValue = i;							// set the keyword number
				LineIndex += Sensors_KW[i].length();		// move the line index to end of keyword
				return true;								// and report back
			}
		}
		KeyWordValue = sensors_none;						// no keyword found
		return false;										// report fail
	}

		private boolean execute_sensors_list(){
			String var = getArrayVarForWrite(TYPE_STRING);				// get the result array variable
			if (var == null) { return false; }							// must name a new string array variable
			if (!checkEOL()) { return false; }							// line must end with ']'

			if (theSensors == null) {
				theSensors = new SensorActivity(this);
			}
			ArrayList<String> census = theSensors.takeCensus();
			int nSensors = census.size();						// If no sensors reported.....
			if (nSensors ==0 ) {
				return RunTimeError("This device reports no Sensors");
			}

			/* Puts the list of sensors into a new array */
			return ListToBasicStringArray(var, census, nSensors);
		}

		private boolean execute_sensors_open(){
			if (theSensors == null) {
				theSensors = new SensorActivity(this);
			}

			if (isEOL()) { return false; }
			boolean toOpen = false;								// anything to open?
			do {												// get the user's list of sensors to open
				if (!evalNumericExpression()) { return false; }	// get the sensor number
				int s = EvalNumericExpressionValue.intValue();
				int d = SensorManager.SENSOR_DELAY_NORMAL;
				if (isNext(':')) {
					if (!evalNumericExpression()) { return false; }	// get the sensor delay value
					d = EvalNumericExpressionValue.intValue();
				}
				toOpen |= theSensors.markForOpen(s, d);			// record selection
			} while (isNext(','));
			if (!checkEOL()) { return false; }

			if (toOpen) {										// if any valid selections
				theSensors.doOpen();							// open selected sensors
			}
			return true;
		}

		private boolean execute_sensors_read(){

			if (theSensors == null) {
				return RunTimeError("Sensors not opened at:");
			}

			if (!evalNumericExpression()) return false;			// Get Sensor Type
			int type = EvalNumericExpressionValue.intValue();
			if (!isNext(',')) return false;

			if (type < 0 || type > SensorActivity.MaxSensors) {
				return RunTimeError("Sensor type not 0 to " + SensorActivity.MaxSensors);
			}

			int[] valueIndex = new int[4];
			if (!getNVar()) return false;						// Sensor Variable
			valueIndex[1] = theValueIndex;
			if (!isNext(',')) return false;

			if (!getNVar()) return false;						// Sensor Variable
			valueIndex[2] = theValueIndex;
			if (!isNext(',')) return false;

			if (!getNVar()) return false;						// Sensor Variable
			valueIndex[3] = theValueIndex;
			if (!checkEOL()) return false;

			double[] SensorValues = theSensors.getValues(type);
			for (int i = 1; i <= 3; ++i) {
				NumericVarValues.set(valueIndex[i], SensorValues[i]); 
			}
			return true;
		}

	private boolean execute_sensors_rotate(){

		/* This is a test. 
		 * It has failed so far
		 * This command has not been exposed to the users.
		 * Someday....?
		 */

		float mOrientation[] = new float[3];
		double SensorValues[];

		SensorValues = theSensors.getValues(1);
		float g[] = new float[3];
		g[0] = (float) SensorValues[1];
		g[1] = (float) SensorValues[2];
		g[2] = (float) SensorValues[3];

		SensorValues = theSensors.getValues(1);
		float m[] = new float[3];
		m[0] = (float) SensorValues[1];
		m[1] = (float) SensorValues[2];
		m[2] = (float) SensorValues[3];

		float r[] = new float[16];
		float R[] = new float[16];
		float i[] = new float[16];

		SensorManager.getRotationMatrix (r, i, g, m);
/*		SensorManager.remapCoordinateSystem(R,
				SensorManager.AXIS_X,
				SensorManager.AXIS_Z, r);*/

		SensorManager.getOrientation(r, mOrientation);

		final float rad2deg = (float)(180.0f/Math.PI);

		if (!getNVar()) return false;
		NumericVarValues.set(theValueIndex, (double) mOrientation[0]);
		if (!isNext(',')) return false;

		if (!getNVar()) return false;
		NumericVarValues.set(theValueIndex, (double) mOrientation[1]);
		if (!isNext(',')) return false;

		if (!getNVar()) return false;
		NumericVarValues.set(theValueIndex, (double) mOrientation[2]);
		if (!checkEOL()) return false;

		return true;
	}

		private boolean execute_sensors_close(){
			if (!checkEOL()) return false;
			if (theSensors != null) {
				theSensors.stop();
				theSensors = null;								// Tell everyone that Sensors are closed
			}
			return true;
		}

// ************************************************ GPS Package ***********************************

	private boolean executeGPS(){
		if (!GetGPSKeyWord()){
			return false;
		}
		if (theGPS == null && KeyWordValue != gps_open) {
			return RunTimeError("GPS not opened at:");
		}
		switch (KeyWordValue) {
			case gps_open:		return execute_gps_open();
			case gps_close:		return execute_gps_close();
			case gps_altitude:	return execute_gps_altitude();
			case gps_longitude:	return execute_gps_longitude();
			case gps_bearing:	return execute_gps_bearing();
			case gps_accuracy:	return execute_gps_accuracy();
			case gps_speed:		return execute_gps_speed();
			case gps_latitude:	return execute_gps_latitude();
			case gps_provider:	return execute_gps_provider();
			case gps_time:		return execute_gps_time();
			default:
				return false;
		}
	}

	private boolean GetGPSKeyWord(){						// Get a GPS command keyword if it is there
															// is the current line index at a keyword?
		for (int i = 0; i < GPS_KW.length; ++i) {			// loop through the keyword list
			if (ExecutingLineBuffer.startsWith(GPS_KW[i], LineIndex)) { // if there is a match
				KeyWordValue = i;							// set the keyword number
				LineIndex += GPS_KW[i].length();			// move the line index to end of keyword
				return true;								// and report back
			}
		}
		KeyWordValue = sensors_none;						// no keyword found
		return false;										// report fail
	}

	public boolean execute_gps_open() {
		if (!checkEOL()) return false;
		if (theGPS != null) return true;					// already opened

		theGPS = new GPS(this);								// start GPS
		return true;
	}
	  
	public boolean execute_gps_close(){
		if (!checkEOL()) return false;
		if (theGPS != null) {
			Log.d(LOGTAG, "Stopping GPS on command");
			theGPS.stop();									// close GPS
			theGPS = null;
		}
		return true;
	}

	private boolean execute_gps_altitude(){
		if (!getNVar()) return false;							// Sensor Variable
		if (!checkEOL()) return false;
		NumericVarValues.set(theValueIndex, GPS.Altitude);		// Set value into variable
		return true;
	}

	private boolean execute_gps_latitude(){
		if (!getNVar()) return false;							// Sensor Variable
		if (!checkEOL()) return false;
		NumericVarValues.set(theValueIndex, GPS.Latitude);
		return true;
	}

	private boolean execute_gps_longitude(){
		if (!getNVar()) return false;							// Sensor Variable
		if (!checkEOL()) return false;
		NumericVarValues.set(theValueIndex, GPS.Longitude);
		return true;
	}

	private boolean execute_gps_bearing(){
		if (!getNVar()) return false;							// Sensor Variable
		if (!checkEOL()) return false;
		NumericVarValues.set(theValueIndex, (double) GPS.Bearing);
		return true;
	}

	private boolean execute_gps_accuracy(){
		if (!getNVar()) return false;							// Sensor Variable
		if (!checkEOL()) return false;
		NumericVarValues.set(theValueIndex, (double) GPS.Accuracy);
		return true;
	}

	private boolean execute_gps_speed(){
		if (!getNVar()) return false;							// Sensor Variable
		if (!checkEOL()) return false;
		NumericVarValues.set(theValueIndex,(double) GPS.Speed);
		return true;
	}

	private boolean execute_gps_time(){
		if (!getNVar()) return false;							// Sensor Variable
		if (!checkEOL()) return false;
		NumericVarValues.set(theValueIndex,(double) GPS.Time);
		return true;
	}

	private boolean execute_gps_provider(){
		if (!getSVar()) return false;
		if (!checkEOL()) return false;
		String provider = GPS.Provider;
		if (provider == null) { provider = ""; }
		StringVarValues.set(theValueIndex, provider);
		return true;
	}

// ********************************************  Array Package  ***************************************
	  
	  private boolean executeARRAY(){
		  DoShuffle = false;
		  DoReverse = false;
		  DoAverage = false;
		  DoVariance = false;
		  DoStdDev = false;
		  DoSort = false;
		  DoSum = false;
		  DoMin = false;
		  DoMax = false;
	    	if (!GetArrayKeyWord()){ return false;}
		    	  switch (KeyWordValue){
		    	  	case array_length:
		    	  		if (!execute_array_length()){return false;}
		    	  		break;
		    	  	case array_load:
		    	  		if (!execute_array_load()){return false;}
		    	  		break;
		    	  	case array_delete:
		    	  		if (!executeUNDIM()){return false;}
		    	  		break;
		    	  	case array_sort:
		    	  		DoSort = true;
		    	  		if (!execute_array_collection()){return false;}
		    	  		break;
		    	  	case array_shuffle:
		    	  		DoShuffle = true;
		    	  		if (!execute_array_collection()){return false;}
		    	  		break;
		    	  	case array_reverse:
		    	  		DoReverse = true;
		    	  		if (!execute_array_collection()){return false;}
		    	  		break;
		    	  	case array_sum:
		    	  		DoSum = true;
		    	  		if (!execute_array_sum()){return false;}
		    	  		break;
		    	  	case array_average:
		    	  		DoAverage = true;
		    	  		if (!execute_array_sum()){return false;}
		    	  		break;
		    	  	case array_min:
		    	  		DoMin = true;
		    	  		if (!execute_array_sum()){return false;}
		    	  		break;
		    	  	case array_max:
		    	  		DoMax = true;
		    	  		if (!execute_array_sum()){return false;}
		    	  		break;
		    	  	case array_variance:
		    	  		DoVariance = true;
		    	  		if (!execute_array_sum())return false;
		    	  		break;
		    	  	case array_std_dev:
		    	  		DoVariance = true;
		    	  		DoStdDev = true;
		    	  		if (!execute_array_sum())return false;
		    	  		break;
		    	  	case array_copy:
		    	  		if (!execute_array_copy()){return false;}
		    	  		break;
		    	  	case array_search:
		    	  		if (!execute_array_search()){return false;}
		    	  		break;
		    	  	default:
		    	  		return false;
		    	  	}
		      	
		  return true;
	  }

	private boolean GetArrayKeyWord(){						// Get an array command keyword if it is there
															// is the current line index at a keyword?
		for (int i = 0; i < Array_KW.length; ++i) {			// loop through the keyword list
			if (ExecutingLineBuffer.startsWith(Array_KW[i], LineIndex)) { // if there is a match
				KeyWordValue = i;							// set the keyword number
				LineIndex += Array_KW[i].length();			// move the line index to end of keyword
				return true;								// and report back
			}
		}
		KeyWordValue = array_none;							// no keyword found
		return false;										// report fail
	}

	private boolean execute_array_length(){

		if (!getNVar()) { return false; }							// Length Variable
		int SaveValueIndex = theValueIndex;

		if (!isNext(',')) { return false; }
		if (getArrayVarForRead() == null) { return false; }			// Get the array variable
		int arrayTableIndex = VarIndex.get(VarNumber);

		Integer[] p = { null, null };
		if (!getIndexPair(p)) return false;							// Get values inside [], if any
		if (!checkEOL()) { return false; }							// line must end with ']'

		if (!getArraySegment(arrayTableIndex, p)) { return false; }	// Get array base and length
		int length = p[1].intValue();

		NumericVarValues.set(SaveValueIndex, (double) length);		// Set the length into the var value

		return true;
	}

	private boolean execute_array_load(){
		String var = getArrayVarForWrite();							// get the result array variable
		if (var == null) { return false; }							// must name a new array variable
		if (!isNext(',')) { return false; }							// Comma before the list

		if (VarIsNumeric) {											// If the array is numeric
			ArrayList<Double> Values = new ArrayList<Double>();		// Create a list for the numeric values
			if (!LoadNumericList(Values)) return false;				// load numeric list
			if (!checkEOL()) return false;
			return ListToBasicNumericArray(var, Values, Values.size()); // Copy the list to a BASIC! array
		} else {
			ArrayList<String> Values = new ArrayList<String>();		// Create a list for the numeric values
			if (!LoadStringList(Values)) return false;				// load string list
			if (!checkEOL()) return false;
			return ListToBasicStringArray(var, Values, Values.size()); // Copy the list to a BASIC! array
		}
	}
	
	private boolean LoadNumericList(ArrayList <Double> Values){
		while (true) {											// loop through the list
			if (!evalNumericExpression()) return false;			// values may be expressions
			Values.add(EvalNumericExpressionValue);

			if (LineIndex >= ExecutingLineBuffer.length()) return false;
			char c = ExecutingLineBuffer.charAt(LineIndex);		// get the next character
			if (c == ',') {										// if it is a comma
				++LineIndex;									// skip it and continue looping
			} else if (c == '~') {								// if it is a line continue character
				if (!nextLine()) return false;					// get next line if there is one
			} else break;										// else no more values in the list
		}
		return true;
	}

	private boolean LoadStringList(ArrayList <String> Values){
		while (true) {											// loop through the list
			if (!getStringArg()) return false;					// values may be expressions
			Values.add(StringConstant);

			if (LineIndex >= ExecutingLineBuffer.length()) return false;
			char c = ExecutingLineBuffer.charAt(LineIndex);		// get the next character
			if (c == ',') {										// if it is a comma
				++LineIndex;									// skip it and continue looping
			} else if (c == '~') {								// if it is a line continue character
				if (!nextLine()) return false;					// get next line if there is one
			} else break;										// else no more values in the list
		}
		return true;
	}

	private boolean execute_array_collection(){

		// This method implements several array commands

		if (getArrayVarForRead() == null) { return false; }				// Get the array variable
		int arrayTableIndex = VarIndex.get(VarNumber);

		Integer[] p = { null, null };
		if (!getIndexPair(p)) return false;								// Get values inside [], if any
		if (!checkEOL()) { return false; }								// line must end with ']'

		if (!getArraySegment(arrayTableIndex, p)) { return false; }		// Get array base and length
		int base = p[0].intValue();
		int length = p[1].intValue();

		if (VarIsNumeric) {											// Numeric Array
			ArrayList <Double> Values = new ArrayList<Double>();	// Create a list to copy array values into

			for (int i = 0; i < length; ++i) {						// Copy the array values into that list
				Values.add(NumericVarValues.get(base+i));
			}
																	// Execute the command specific procedure
			if (DoReverse)		{ Collections.reverse(Values); }	// Reverse
			else if (DoSort)	{ Collections.sort(Values); }		// Sort
			else if (DoShuffle)	{ Collections.shuffle(Values); }	// Shuffle

			for (int i =0; i<length; ++i) {							// Copy the results back to the array
				NumericVarValues.set(base + i, Values.get(i));
			}

		} else {													// Do the same stuff for a string array
			ArrayList<String> Values = new ArrayList<String>();
			for (int i = 0; i < length; ++i) {
				Values.add(StringVarValues.get(base + i));
			}

			if (DoReverse)		{ Collections.reverse(Values); }	// Reverse
			else if (DoSort)	{ Collections.sort(Values); }		// Sort
			else if (DoShuffle)	{ Collections.shuffle(Values); }	// Shuffle

			for (int i = 0; i < length; ++i) {
				StringVarValues.set(base + i, Values.get(i));
			}
		}

		return true;
	}

	private boolean execute_array_sum(){

		if (!getNVar()) { return false; }							// The value return variable
		int SaveValueIndex = theValueIndex;

		if (!isNext(',')) { return false; }
		if (getArrayVarForRead() == null) { return false; }			// Get the array variable
		if (!VarIsNumeric) { return RunTimeError(EXPECT_NUM_ARRAY); }
		int arrayTableIndex = VarIndex.get(VarNumber);

		Integer[] p = { null, null };
		if (!getIndexPair(p)) return false;							// Get values inside [], if any
		if (!checkEOL()) { return false; }							// line must end with ']'

		if (!getArraySegment(arrayTableIndex, p)) { return false; }	// Get array base and length
		int base = p[0].intValue();
		int length = p[1].intValue();

		double Sum = 0;
		double Min = NumericVarValues.get(base);
		double Max = NumericVarValues.get(base);

		for (int i = 0; i < length; ++i){							// Loop through the array values
			double d = NumericVarValues.get(base+i);				// Pick up the elements value
			Sum = Sum + d;											// build the Sum
			if (d < Min) { Min = d; }								// find the minimum value
			if (d > Max) { Max = d; }								// and the maxium value
		}

		double Average = Sum / length;								// Calculate the average
																	// Set the return value according to the command
		if (DoAverage)	{ NumericVarValues.set(SaveValueIndex, Average); }
		else if (DoSum)	{ NumericVarValues.set(SaveValueIndex, Sum); }
		else if (DoMax)	{ NumericVarValues.set(SaveValueIndex, Max); }
		else if (DoMin)	{ NumericVarValues.set(SaveValueIndex, Min); }
		else if (DoVariance) {
				double T = 0;
				double W = 0;
				for (int i = 0; i <length; ++i) {
					double d = NumericVarValues.get(base + i);		// Pick up the elements value
					W = d - Average;
					T = T + W*W;
				}
				double variance = T/(length-1);
				double sd = Math.sqrt(variance);
				if (DoStdDev) NumericVarValues.set(SaveValueIndex, sd);
				else NumericVarValues.set(SaveValueIndex, variance);
		}

		return true;
	}

	private boolean execute_array_copy(){
															// **** Source Array ****

		if (getVarAndType() == null)			{ return false; }	// get the array variable
		if (!VarIsArray)						{ return RunTimeError("Source not array"); }
		if (VarIsNew) 							{ return RunTimeError("Source array not DIMed"); }
		boolean SourceArrayNumeric = VarIsNumeric;
		int arrayTableIndex = VarIndex.get(VarNumber);

		Integer[] p = { null, null };
		if (!getIndexPair(p))					{ return false; }	// get values inside [], if any

		if (!getArraySegment(arrayTableIndex, p)) { return false; }	// Get array base and length
		int SourceBase = p[0].intValue();
		int SourceLength = p[1].intValue();

		if (!isNext(','))						{ return false; }
															// *** Destination Array ***

		String destVar = getVarAndType();							// Get the array variable
		if (destVar == null)					{ return false; }
		if (!VarIsArray)						{ return RunTimeError("Destination not array"); }
		if (SourceArrayNumeric != VarIsNumeric)	{ return RunTimeError("Arrays not of same type"); }
		boolean destIsNew = VarIsNew;
		int destVarNumber = VarNumber;								// Not valid if destIsNew!

		int extras = 0;												// Get the extras parameter
		if (!isNext(']')) {
			if (!evalNumericExpression())		{ return false; }
			if (!isNext(']'))					{ return false; }
			extras = EvalNumericExpressionValue.intValue();
		}
		if (!checkEOL())						{ return false; }

		int destStart = 0;
		int totalLength = 0;
		if (destIsNew) {											// copy to new array, optional extras arg adds element(s)

			if (extras == 0) {										// check for cases that would create empty array
				if (SourceLength < 1) { return RunTimeError("Source array [Start,Length] must specify at least one element"); }
			}
			totalLength = SourceLength + Math.abs(extras);			// go build a new array of the proper size and type
			if (!BuildBasicArray(destVar, SourceArrayNumeric, totalLength)) { return false; }
			destStart = ArrayValueStart;
			if (extras < 0) { destStart -= extras; }				// if negative extras, add absolute value to start index

		} else {													// copy over old array, optional extras arg is start index
			if (SourceLength < 1)				{ return true; }	// nothing to do

			Bundle ArrayEntry = ArrayTable.get(VarIndex.get(destVarNumber)); // Get the array table bundle for this array
			int destBase = ArrayEntry.getInt("base");				// and the start of the array in the variable space
			int destLength = ArrayEntry.getInt("length");			// get the destination array length
			if (extras > destLength)			{ return true; }	// dest start is past end of array, nothing to do

			if (--extras < 0) { extras = 0; }						// convert to 0-based index, ignore if less than 1
			if (extras + SourceLength > destLength) {				// start index + length to copy > space available
				SourceLength = destLength - extras;					// limit copy
			} else {
				destLength = extras + SourceLength;
				// ArrayEntry.putInt("length", destLength);			// shorten destination array
			}
			destStart = destBase + extras;
			totalLength = destLength - extras;
		}

		if (SourceArrayNumeric) {									// Do numeric array
			for (int i = 0; i < SourceLength; ++i) {				// Copy the source array values
				NumericVarValues.set(destStart++, NumericVarValues.get(SourceBase + i));
			}
		} else {													// Do String array
			for (int i = 0; i < SourceLength; ++i) {				// Copy the source array values
				StringVarValues.set(destStart++, StringVarValues.get(SourceBase + i));
			}
		}
		return true;
	}

	private boolean execute_array_search(){
		if (getArrayVarForRead() == null) { return false; }			// Get the array variable
		boolean isNumeric = VarIsNumeric;
		int arrayTableIndex = VarIndex.get(VarNumber);

		Integer[] p = { null, null };
		if (!getIndexPair(p)) return false;							// Get values inside [], if any

		if (!getArraySegment(arrayTableIndex, p)) { return false; }	// Get array base and length
		int base = p[0].intValue();
		int length = p[1].intValue();

		if (!isNext(',')) return false;								// move to the value

		int found = -1;
		int savedVarIndex = 0;
		int start = 0;

		if (!isNumeric) {											// String type array
			if (!getStringArg()) return false;						// Get the string to search for
			String sfind = StringConstant;

			if (!isNext(',')) return false;							// move to the result var
			if (!getNVar()) return false;
			savedVarIndex = theValueIndex;

			if (isNext(',')) {										// move to the start index
				if (!evalNumericExpression()) return false;
				start = EvalNumericExpressionValue.intValue();
				if (--start < 0) { start = 0; }						// convert to zero-based index
			}
			if (!checkEOL()) return false;

			for (int i = start; i < length; ++i) {					// Search the list for a match
				if (sfind.equals(StringVarValues.get(base + i))) {
					found = i;
					break;
				}
			}
		} else {													// Numeric type array
			if (!evalNumericExpression()) return false;				// Get the value to search for
			double nfind = EvalNumericExpressionValue;

			if (!isNext(',')) return false;							// move to the result var
			if (!getNVar()) return false;
			savedVarIndex = theValueIndex;

			if (isNext(',')) {										// move to the start index
				if (!evalNumericExpression()) return false;
				start = EvalNumericExpressionValue.intValue();
				if (--start < 0) { start = 0; }						// convert to zero-based index
			}
			if (!checkEOL()) return false;

			for (int i = start; i < length; ++i) {					// Search the list for a match
				if (nfind == NumericVarValues.get(base + i)) {
					found = i;
					break;
				}
			}
		}

		++found;// Found is ones based
		NumericVarValues.set(savedVarIndex, (double)found);

		return true;
	}

//***********************************  List Package ************************************************

	private boolean executeLIST(){
		if (GetListKeyWord()) {
			switch (KeyWordValue) {
				case list_new:		return execute_LIST_NEW();
				case list_addarray:	return execute_LIST_ADDARRAY();
				case list_addlist:	return execute_LIST_ADDLIST();
				case list_add:		return execute_LIST_ADD();
				case list_set:		return execute_LIST_SET();
				case list_get:		return execute_LIST_GET();
				case list_gettype:	return execute_LIST_GETTYPE();
				case list_clear:	return execute_LIST_CLEAR();
				case list_remove:	return execute_LIST_REMOVE();
				case list_insert:	return execute_LIST_INSERT();
				case list_size:		return execute_LIST_SIZE();
				case list_contains:	return execute_LIST_CONTAINS();
				case list_toarray:	return execute_LIST_TOARRAY();
				case list_search:	return execute_LIST_SEARCH();
				default: break;
			}
		}
		return false;
	}

	private boolean GetListKeyWord(){						// Get a list command keyword if it is there
															// is the current line index at a keyword?
		for (int i = 0; i < List_KW.length; ++i) {			// loop through the keyword list
			if (ExecutingLineBuffer.startsWith(List_KW[i], LineIndex)) { // if there is a match
				KeyWordValue = i;							// set the keyword number
				LineIndex += List_KW[i].length();			// move the line index to end of keyword
				return true;								// and report back
			}
		}
		KeyWordValue = list_none;							// no keyword found
		return false;										// report fail
	}

	private boolean execute_LIST_NEW(){
		char c = ExecutingLineBuffer.charAt(LineIndex);    // Get the type, s or n
		++LineIndex;
		int type = 0;
		
		if (c == 'n' ){
			type = list_is_numeric;
		} 
		else if (c == 's'){
			type = list_is_string;
		} else return false;

		if (!isNext(',')) { return false; }
		
		if (!getNVar()) { return false; }								// List pointer variable
		int SaveValueIndex = theValueIndex;

		if (!checkEOL()) { return false; }

		int theIndex = createNewList(type);								// Try to create list
		if (theIndex < 0) { return false; }								// Create failed

		NumericVarValues.set(SaveValueIndex, (double) theIndex);		// Return the list pointer
		return true;
	}

	private int createNewList(int type) {						// Put a new ArrayList in global theLists
																// Put its type in global theListsType 
		int index = theLists.size();
		if (type == list_is_string) {									// Create a string list
			theLists.add(new ArrayList <String>());
		} else if (type == list_is_numeric) {							// Create a numeric list
			theLists.add(new ArrayList <Double>());
		} else {
			return -1;													// Unknown type, don't create anything
		}
		theListsType.add(type);											// Add the type
		return index;
	}

	private int getListArg() {											// Get the List pointer
		return getListArg("Invalid List Pointer");
	}

	private int getListArg(String errorMsg) {							// Get the List pointer
		if (evalNumericExpression()) {
			int listIndex = EvalNumericExpressionValue.intValue();
			if ((listIndex > 0) && (listIndex < theLists.size())) {
				return listIndex;
			}
			RunTimeError(errorMsg);
		}
		return 0;
	}

	private boolean execute_LIST_ADDARRAY(){
		int listIndex = getListArg();								// Get the list pointer
		if (listIndex == 0) return false;

		if (!isNext(',')) return false;
		if (getArrayVarForRead() == null) return false;				// Get the array variable
		int arrayTableIndex = VarIndex.get(VarNumber);

		Integer[] p = { null, null };
		if (!getIndexPair(p)) return false;							// Get values inside [], if any
		if (!checkEOL()) return false;

		boolean isListNumeric = (theListsType.get(listIndex) == list_is_numeric);
		if (isListNumeric != VarIsNumeric) { return RunTimeError("Type mismatch"); }

		if (!getArraySegment(arrayTableIndex, p)) { return false; }	// Get array base and length
		int base = p[0].intValue();
		int length = p[1].intValue();

		ArrayList destList = theLists.get(listIndex);
		ArrayList sourceList = (isListNumeric) ? NumericVarValues : StringVarValues;
		for (int i = 0; i < length; ++i ) {							// Copy array to list
			destList.add(sourceList.get(base + i));
		}
		return true;
	}

	private boolean execute_LIST_ADDLIST(){
		int destListIndex = getListArg("Invalid Destination List Pointer");	// Get the destination list pointer
		if (destListIndex == 0) return false;
		if (!isNext(',')) return false;

		int sourceListIndex = getListArg("Invalid Source List Pointer");	// Get the source list pointer
		if (sourceListIndex == 0) return false;
		if (!checkEOL()) return false;

		boolean isDestNumeric = (theListsType.get(destListIndex) == list_is_numeric);
		boolean isSourceNumeric = (theListsType.get(sourceListIndex) == list_is_numeric);
		if (isDestNumeric != isSourceNumeric) { return RunTimeError("Type mismatch"); }

		theLists.get(destListIndex).addAll(theLists.get(sourceListIndex));
		return true;
	}

	private boolean execute_LIST_SEARCH(){
		int listIndex = getListArg();									// Get the list pointer
		if (listIndex == 0) return false;
		if (!isNext(',')) return false;									// move to the value

		int found = -1;
		int savedVarIndex = 0;
		int start = 0;

		switch (theListsType.get(listIndex))
		{
		case list_is_string:											 // String type list
			ArrayList<String> SValues = theLists.get(listIndex);		 // Get the string list
			if (!getStringArg()) return false;							 // values may be expressions
			String sfind = StringConstant;

			if (!isNext(',')) return false;								 // move to the result var
			if (!getNVar()) return false;
			savedVarIndex = theValueIndex;

			if (isNext(',')) {											// move to the start index
				if (!evalNumericExpression()) return false;
				start = EvalNumericExpressionValue.intValue();
				if (--start < 0) { start = 0; }						// convert to zero-based index
			}
			if (!checkEOL()) return false;

			for (int i = start; i < SValues.size(); ++i){			// Search the list for a match
				if (sfind.equals(SValues.get(i))){
					found = i;
					break;
				}
			}
			break;

		case list_is_numeric:
			ArrayList<Double> NValues = theLists.get(listIndex);	// Get the numeric list
			if (!evalNumericExpression()) return false;             // values may be expressions
			double nfind = EvalNumericExpressionValue;

			if (!isNext(',')) return false;								// move to the result var
			if (!getNVar()) return false;
			savedVarIndex = theValueIndex;

			if (isNext(',')) {											// move to the start index
				if (!evalNumericExpression()) return false;
				start = EvalNumericExpressionValue.intValue();
				if (--start < 0) { start = 0; }						// convert to zero-based index
			}
			if (!checkEOL()) return false;

			for (int i = start; i < NValues.size(); ++i){           // Search the list for a match
				if (nfind == (NValues.get(i))){
					found = i;
					break;
				}
			}
			break;

		default:
			return RunTimeError("Internal problem. Notify developer");
		}

		++found;// Found is ones based

		NumericVarValues.set(savedVarIndex, (double) found);
		return true;
	}

	private boolean execute_LIST_ADD(){
		int listIndex = getListArg();								// Get the list pointer
		if (listIndex == 0) return false;
		if (!isNext(',')) return false;								// move to the result value

		boolean isListNumeric = (theListsType.get(listIndex) == list_is_numeric);
		if (isListNumeric) {
			ArrayList<Double> Values = theLists.get(listIndex);		// Get the numeric list
			if (!LoadNumericList(Values)) return false;				// load numeric list
		} else {
			ArrayList<String> Values = theLists.get(listIndex);		// Get the string list
			if (!LoadStringList(Values)) return false;				// load string list
		}
		return checkEOL();
	}

	private boolean execute_LIST_SET(){
		int listIndex = getListArg();								// Get the list pointer
		if (listIndex == 0) return false;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;					// Get the index to get
		int getIndex = EvalNumericExpressionValue.intValue();
		--getIndex;													// Ones based for Basic user

		if (!isNext(',')) return false;

		switch (theListsType.get(listIndex))						// Get this lists type
		{
		case list_is_string:										// String
			if (!evalStringExpression()) {
				return RunTimeError("Type mismatch");
			}
			if (!checkEOL()) return false;
			ArrayList<String> thisStringList = theLists.get(listIndex);  // Get the string list
			if (getIndex < 0 || getIndex >= thisStringList.size()){		
				return RunTimeError("Index out of bounds");
			}
			thisStringList.set(getIndex, StringConstant);
			break;

		case list_is_numeric:												// Number
			if (!evalNumericExpression()){
				return RunTimeError("Type mismatch");
			}
			if (!checkEOL()) return false;
			ArrayList<Double> thisNumericList = theLists.get(listIndex);	//Get the numeric list
			if (getIndex < 0 || getIndex >= thisNumericList.size()){
				return RunTimeError("Index out of bounds");
			}
			thisNumericList.set(getIndex, EvalNumericExpressionValue);
			break;

		default:
			return RunTimeError("Internal problem. Notify developer");
		}

		return true;
	}

	private boolean execute_LIST_GET(){
		int listIndex = getListArg();								// Get the list pointer
		if (listIndex == 0) return false;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;					// Get the index to get
		int getIndex = EvalNumericExpressionValue.intValue();
		--getIndex;													// Ones based for Basic user

		if (!isNext(',')) return false;
		if (!getVar()) return false;								// Get the return value variable
		if (!checkEOL()) return false;

		int listType = theListsType.get(listIndex);					// Get this lists type
		boolean isListNumeric = (listType == list_is_numeric);
		if (isListNumeric != VarIsNumeric) { return RunTimeError("Type mismatch"); }

		switch (listType)
		{
		case list_is_string:										// String
			ArrayList<String> thisStringList = theLists.get(listIndex);		// Get the string list
			if (getIndex < 0 || getIndex >= thisStringList.size()){
				return RunTimeError("Index out of bounds");
			}
			String thisString = thisStringList.get(getIndex);				// Get the requested string
			StringVarValues.set(theValueIndex, thisString);
			break;

		case list_is_numeric:										// Number
			ArrayList<Double> thisNumericList = theLists.get(listIndex);	// Get the numeric list
			if (getIndex < 0 || getIndex >= thisNumericList.size()){
				return RunTimeError("Index out of bounds");
			}
			Double thisNumber = thisNumericList.get(getIndex);				// Get the requested number
			NumericVarValues.set(theValueIndex, thisNumber);
			break;

		default:
			return RunTimeError("Internal problem. Notify developer");
		}
		
		return true;
	}

	private boolean execute_LIST_GETTYPE(){
		int listIndex = getListArg();								// Get the list pointer
		if (listIndex == 0) return false;
		if (!isNext(',')) return false;

		if (!getSVar()) return false;
		if (!checkEOL()) return false;
		
		switch (theListsType.get(listIndex))						// Get this lists type
		{
		case list_is_string:										// String
			StringVarValues.set(theValueIndex, "S");
			break;

		case list_is_numeric:										// Number
			StringVarValues.set(theValueIndex, "N");
			break;

		default:
			return RunTimeError("Internal problem. Notify developer");
		}
		return true;
	}

	private boolean execute_LIST_CLEAR(){
		int listIndex = getListArg();								// Get the list pointer
		if (listIndex == 0) return false;
		if (!checkEOL()) return false;
		theLists.get(listIndex).clear();
		return true;
	}

	private boolean execute_LIST_REMOVE(){
		int listIndex = getListArg();								// Get the list pointer
		if (listIndex == 0) return false;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;					// Get the index to remove
		if (!checkEOL()) return false;

		int getIndex = EvalNumericExpressionValue.intValue();
		--getIndex;													// Ones based for Basic user

		ArrayList theList = theLists.get(listIndex);				// Get the  list
		if (getIndex < 0 || getIndex >= theList.size()) {
			return RunTimeError("Index out of bounds");
		}
		theList.remove(getIndex);
		return true;
	}

	private boolean execute_LIST_INSERT(){
		int listIndex = getListArg();								// Get the list pointer
		if (listIndex == 0) return false;
		if (!isNext(',')) return false;

		if (!evalNumericExpression()) return false;					// Get the index insert at
		int getIndex = EvalNumericExpressionValue.intValue();
		--getIndex;													// Ones based for Basic user

		if (!isNext(',')) return false;

		switch (theListsType.get(listIndex))						// Get this lists type
		{
		case list_is_string:											 // String
			if (!getStringArg()) {
				return RunTimeError("Type mismatch");
			}
			if (!checkEOL()) return false;

			ArrayList<String> thisStringList = theLists.get(listIndex);	// Get the string list
			if (getIndex < 0 || getIndex > thisStringList.size()) {		// if index == size element goes at end of list
				return RunTimeError("Index out of bounds");
			}
			thisStringList.add(getIndex, StringConstant);
			break;

		case list_is_numeric:											// Number
			if (!evalNumericExpression()){
				return RunTimeError("Type mismatch");
			}
			if (!checkEOL()) return false;

			ArrayList<Double> thisNumericList = theLists.get(listIndex);	// Get the numeric list
			if (getIndex < 0 || getIndex > thisNumericList.size()) {	// if index == size element goes at end of list
				return RunTimeError("Index out of bounds");
			}
			thisNumericList.add(getIndex, EvalNumericExpressionValue);
			break;

		default:
			return RunTimeError("Internal problem. Notify developer");
		}

		return true;
	}

	private boolean execute_LIST_SIZE(){
		int listIndex = getListArg();									// Get the list pointer
		if (listIndex == 0) return false;
		if (!isNext(',')) return false;									// move to the return var

		if (!getNVar()) return false;
		if (!checkEOL()) return false;
		
		int size = theLists.get(listIndex).size();
		NumericVarValues.set(theValueIndex, (double) size);

		return true;
	}

	private boolean execute_LIST_CONTAINS(){
		return false;
	}

	private boolean execute_LIST_TOARRAY(){
		int listIndex = getListArg();									// Get the list pointer
		if (listIndex == 0) return false;
		if (!isNext(',')) return false;									// move to the array var

		String var = getArrayVarForWrite();								// get the result array variable
		if (var == null) return false;									// must name a new array variable
		if (!checkEOL()) return false;									// line must end with ']'

		int listType = theListsType.get(listIndex);						// Get this lists type
		boolean isListNumeric = (listType == list_is_numeric);
		if (isListNumeric != VarIsNumeric) { return RunTimeError("Type mismatch"); }

		if (isListNumeric) {
			ArrayList<Double> Values = theLists.get(listIndex);			// Get the numeric list
			return ListToBasicNumericArray(var, Values, Values.size());	// Copy the list to a BASIC! array
		} else {
			ArrayList<String> Values = theLists.get(listIndex);			// Get the string list
			return ListToBasicStringArray(var, Values, Values.size());	// Copy the list to a BASIC! array
		}
	}

// ***************************************Bundle Commands *******************************************

	private boolean executeBUNDLE(){
		if (GetBundleKeyWord()) {
			switch (KeyWordValue) {
				case bundle_create:	return execute_BUNDLE_CREATE();
				case bundle_put:	return execute_BUNDLE_PUT();
				case bundle_get:	return execute_BUNDLE_GET();
				case bundle_type:	return execute_BUNDLE_TYPE();
				case bundle_keyset:	return execute_BUNDLE_KEYSET();
				case bundle_copy:	return execute_BUNDLE_COPY();
				case bundle_clear:	return execute_BUNDLE_CLEAR();
				case bundle_contain:return execute_BUNDLE_CONTAIN();	// Condor
				default: break;
			}
		}
		return false;
	}

	private boolean GetBundleKeyWord(){						// Get a bundle command keyword if it is there
															// is the current line index at a keyword?
		for (int i = 0; i < Bundle_KW.length; ++i) {		// loop through the keyword list
			if (ExecutingLineBuffer.startsWith(Bundle_KW[i], LineIndex)) { // if there is a match
				KeyWordValue = i;							// set the keyword number
				LineIndex += Bundle_KW[i].length();			// move the line index to end of keyword
				return true;								// and report back
			}
		}
		KeyWordValue = list_none;							// no keyword found
		return false;										// report fail
	}

	private boolean execute_BUNDLE_CREATE(){

		if (!getNVar()) return false;							// Bundle pointer variable
		if (!checkEOL()) return false;
		int SaveValueIndex = theValueIndex;
		Bundle b = new Bundle();

		int bundleIndex = theBundles.size();
		theBundles.add(b);

		NumericVarValues.set(SaveValueIndex, (double) bundleIndex);

		return true;
	}

	private int getBundleArg() {										// Get the Bundle pointer
		if (evalNumericExpression()) {
			int bundleIndex = EvalNumericExpressionValue.intValue();
			if ((bundleIndex > 0) && (bundleIndex < theBundles.size())) {
				return bundleIndex;
			}
			RunTimeError("Invalid Bundle Pointer");
		}
		return 0;
	}

	private boolean execute_BUNDLE_PUT(){
		int bundleIndex = getBundleArg();								// Get the Bundle pointer
		if (bundleIndex == 0) return false;

		if (!isNext(',')) return false;									// move to the tag
		if (!getStringArg()) return false;
		String tag = StringConstant;

		if (!isNext(',')) return false;									// move to the value

		Bundle b = theBundles.get(bundleIndex);

		int LI = LineIndex;
		if (evalNumericExpression()) {
			if (!checkEOL()) return false;
			b.putDouble(tag, EvalNumericExpressionValue);
			b.putBoolean("@@@N." + tag, true);
		} else {
			LineIndex = LI;
			if (!getStringArg()) return false;
			if (!checkEOL()) return false;
			b.putString(tag, StringConstant);
			b.putBoolean("@@@N." + tag, false);
		}
		return true;
	}

	private boolean execute_BUNDLE_GET(){
		int bundleIndex = getBundleArg();								// Get the Bundle pointer
		if (bundleIndex == 0) return false;

		if (!isNext(',')) return false;									// move to the tag
		if (!getStringArg()) return false;
		String tag = StringConstant;

		if (!isNext(',')) return false;									// move to the value
		if (!getVar()) return false;
		if (!checkEOL()) return false;

		Bundle b = theBundles.get(bundleIndex);
		if (!b.containsKey(tag)) {
			return RunTimeError(tag + " not in bundle");
		}

		boolean isNumeric = b.getBoolean("@@@N." + tag);
		if (isNumeric) {
			if (!VarIsNumeric) {
				return RunTimeError(tag + " is not a string");
			}
			NumericVarValues.set(theValueIndex, b.getDouble(tag));
		} else {
			if (VarIsNumeric) {
				return RunTimeError(tag + " is not numeric");
			}
			StringVarValues.set(theValueIndex, b.getString(tag));
		}
		return true;
	}

	private boolean execute_BUNDLE_TYPE(){
		int bundleIndex = getBundleArg();								// Get the Bundle pointer
		if (bundleIndex == 0) return false;

		if (!isNext(',')) return false;									// move to the tag
		if (!getStringArg()) return false;
		String tag = StringConstant;

		if (!isNext(',')) return false;									// move to the result var
		if (!getSVar()) return false;
		if (!checkEOL()) return false;

		Bundle b = theBundles.get(bundleIndex);
		if (!b.containsKey(tag)) {
			return RunTimeError(tag + " not in bundle");
		}

		boolean isNumeric = b.getBoolean("@@@N." + tag);
		String type = (isNumeric) ? "N" : "S";

		StringVarValues.set(theValueIndex, type);
		return true;
	}

	private boolean execute_BUNDLE_KEYSET(){
		int bundleIndex = getBundleArg();								// Get the Bundle pointer
		if (bundleIndex == 0) return false;

		if (!isNext(',')) return false;									// move to the list var
		if (!getNVar()) return false;
		if (!checkEOL()) return false;

		 ArrayList<String> theStringList = new ArrayList <String>();
		 int theIndex = theLists.size();
		 theLists.add(theStringList);
		 theListsType.add(list_is_string);
		 NumericVarValues.set(theValueIndex, (double) theIndex);

		Bundle b = theBundles.get(bundleIndex);
		Set<String> set= b.keySet();

		for (String s : set) {
			if (!s.startsWith("@@@N.")) { theStringList.add(s); }
		}

		return true;
	}

	private boolean execute_BUNDLE_COPY(){
		return false;
	}

	private boolean execute_BUNDLE_CLEAR(){
		int bundleIndex = getBundleArg();								// Get the Bundle pointer
		if (bundleIndex == 0) return false;
		if (!checkEOL()) return false;

		Bundle b = theBundles.get(bundleIndex);
		b.clear();
		return true;
	}

	private boolean execute_BUNDLE_CONTAIN(){
		int bundleIndex = getBundleArg();								// Get the Bundle pointer
		if (bundleIndex == 0) return false;

		if (!isNext(',')) return false;									// move to the tag
		if (!getStringArg()) return false;
		String tag = StringConstant;

		if (!isNext(',')) return false;									// move to the result var
		if (!getNVar()) return false;
		if (!checkEOL()) return false;

		Bundle b = theBundles.get(bundleIndex);
		double thereis = (b.containsKey(tag)) ? 1.0 : 0.0;
		NumericVarValues.set(theValueIndex, thereis);

		return true;
	}

// ************************************** Stack Commands ********************************************

	private boolean executeSTACK(){
		if (GetStackKeyWord()) {
			switch (KeyWordValue) {
				case stack_create:	return execute_STACK_CREATE();
				case stack_push:	return execute_STACK_PUSH();
				case stack_pop:		return execute_STACK_POP();
				case stack_peek:	return execute_STACK_PEEK();
				case stack_type:	return execute_STACK_TYPE();
				case stack_isempty:	return execute_STACK_ISEMPTY();
				case stack_clear:	return execute_STACK_CLEAR();
				default: break;
			}
		}
		return false;
	}

	private boolean GetStackKeyWord(){						// Get a Basic keyword if it is there
															// is the current line index at a keyword?
		for (int i = 0; i < Stack_KW.length; ++i) {			// loop through the keyword list
			if (ExecutingLineBuffer.startsWith(Stack_KW[i], LineIndex)) { // if there is a match
				KeyWordValue = i;							// set the keyword number
				LineIndex += Stack_KW[i].length();			// move the line index to end of keyword
				return true;								// and report back
			}
		}
		KeyWordValue = list_none;							// no keyword found
		return false;										// report fail
	}

	private boolean execute_STACK_CREATE(){
		char c = ExecutingLineBuffer.charAt(LineIndex);		// Get the type, s or n
		++LineIndex;
		int type = 0;
		
		if (c == 'n' ){
			type = stack_is_numeric;
		} 
		else if (c == 's'){
			type = stack_is_string;
		} else return false;

		if (!isNext(',')) { return false; }

		if (!getNVar()) { return false; }								// Stack pointer variable
		int SaveValueIndex = theValueIndex;

		if (!checkEOL()) { return false; }

	 Stack theStack = new Stack();
	 int theIndex = theStacks.size();
	 theStacks.add(theStack);

	   theStacksType.add(type);											// add the type
	   NumericVarValues.set(SaveValueIndex, (double) theIndex);   		// Return the stack pointer    

		return true;
	}

	private int getStackIndexArg() {									// Get the Stack pointer
		if (evalNumericExpression()) {
			int stackIndex = EvalNumericExpressionValue.intValue();
			if ((stackIndex > 0) && (stackIndex < theStacks.size())) {
				return stackIndex;
			}
			RunTimeError("Invalid Stack Pointer");
		}
		return 0;
	}

	private boolean execute_STACK_PUSH(){
		int stackIndex = getStackIndexArg();								// Get the Stack pointer
		if (stackIndex == 0) return false;
		if (!isNext(',')) return false;										// move to the value

		Stack thisStack = theStacks.get(stackIndex);						// Get the stack
		switch (theStacksType.get(stackIndex))
		{
		case stack_is_string:												// String type stack
			if (!getStringArg()) {
				return RunTimeError("Type mismatch");
			}
			if (!checkEOL()) return false;
			thisStack.push(StringConstant);									// Add the string to the stack
			break;

		case stack_is_numeric:
			if (!evalNumericExpression()){
				return RunTimeError("Type mismatch");
			}
			if (!checkEOL()) return false;
			thisStack.push(EvalNumericExpressionValue);						// Add the value to the stack
			break;

		default:
			return RunTimeError("Internal problem. Notify developer");
		}
		return true;
	}

	private boolean execute_STACK_POP(){
		int stackIndex = getStackIndexArg();								// Get the Stack pointer
		if (stackIndex == 0) return false;
		if (!isNext(',')) return false;										// move to the value

		Stack thisStack = theStacks.get(stackIndex);		 				// Get the Stack
		if (thisStack.isEmpty()){
			return RunTimeError("Stack is empty");
		}

		switch (theStacksType.get(stackIndex))
		{
		case stack_is_string:												// String type stack
			if (!getSVar()) {
				return RunTimeError("Type mismatch");
			}
			if (!checkEOL()) return false;
			String thisString = (String) thisStack.pop();
			StringVarValues.set(theValueIndex, thisString);
			break;

		case stack_is_numeric:
			if (!getNVar()) {
				return RunTimeError("Type mismatch");
			}
			if (!checkEOL()) return false;
			Double thisNumber = (Double) thisStack.pop();
			NumericVarValues.set(theValueIndex, thisNumber);
			break;

		default:
			return RunTimeError("Internal problem. Notify developer");
		}
		return true;
	}

	private boolean execute_STACK_PEEK(){
		int stackIndex = getStackIndexArg();								// Get the Stack pointer
		if (stackIndex == 0) return false;
		if (!isNext(',')) return false;										// move to the value

		Stack thisStack = theStacks.get(stackIndex);						// Get the Stack
		if (thisStack.isEmpty()){
			return RunTimeError("Stack is empty");
		}

		switch (theStacksType.get(stackIndex))
		{
		case stack_is_string:												// String type stack
			if (!getSVar()) {
				return RunTimeError("Type mismatch");
			}
			if (!checkEOL()) return false;
			String thisString = (String) thisStack.peek();
			StringVarValues.set(theValueIndex, thisString);
			break;

		case stack_is_numeric:
			if (!getNVar()) {
				return RunTimeError("Type mismatch");
			}
			if (!checkEOL()) return false;
			Double thisNumber = (Double) thisStack.peek();
			NumericVarValues.set(theValueIndex, thisNumber);
			break;

		default:
			return RunTimeError("Internal problem. Notify developer");
		}
		return true;
	}

	private boolean execute_STACK_TYPE(){
		int stackIndex = getStackIndexArg();								// Get the Stack pointer
		if (stackIndex == 0) return false;
		if (!isNext(',')) return false;										// move to the value
		if (!getSVar()) return false;
		if (!checkEOL()) return false;

		switch (theStacksType.get(stackIndex))						// Get this Stack type
		{
		case stack_is_string:										// String
			StringVarValues.set(theValueIndex, "S");
			break;

		case stack_is_numeric:												// Number
			StringVarValues.set(theValueIndex, "N");
			break;

		default:
			return RunTimeError("Internal problem. Notify developer");
		}
		return true;
	}

	private boolean execute_STACK_ISEMPTY(){
		int stackIndex = getStackIndexArg();								// Get the Stack pointer
		if (stackIndex == 0) return false;
		if (!isNext(',')) return false;
		if (!getNVar()) return false;
		if (!checkEOL()) return false;

		Stack thisStack = theStacks.get(stackIndex);						// Get the Stack
		double empty = thisStack.isEmpty() ? 1 : 0;
		NumericVarValues.set(theValueIndex, empty);

		return true;
	}

	private boolean execute_STACK_CLEAR(){
		int stackIndex = getStackIndexArg();								// Get the Stack pointer
		if (stackIndex == 0) return false;
		if (!checkEOL()) return false;

		Stack thisStack = theStacks.get(stackIndex);						// Get the Stack
		while (!thisStack.isEmpty()) { thisStack.pop(); }

		return true;
	}


	//*********************************** Clipboard Commands *****************************************

	/*
	// This code does not work on devices with API level < 11
	 
	private boolean executeCLIPBOARD_GET(){
		  if (!getSVar()) return false;						// get the var to put the clip into
			ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
		  String data = "";
		  if (clipboard.hasPrimaryClip()){                        // If clip board has text
			  CharSequence data1 = clipboard.getText();    // Get the clip
			  data = data1.toString(); 
			  if (data == null) data = "";
		  }else data ="";									// If no clip, set data to null
		  StringVarValues.set(theValueIndex, data);         // Return the result to user
			if (!checkEOL()) return false;
		return true;
	}
	
	private boolean executeCLIPBOARD_PUT(){
		int v = Integer.valueOf(Build.VERSION.SDK_INT);
		if (!getStringArg()) return false;          // Get the string to put into the clipboard
		ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
		CharSequence cs = StringConstant;
		ClipData clip = ClipData.newPlainText("simple text",cs);
		clipboard.setPrimaryClip(clip);
		if (!checkEOL()) return false;
		return true;
	}
	*/
	
	private boolean executeCLIPBOARD_GET(){
		if (!getSVar()) return false;						// get the var to put the clip into
		if (!checkEOL()) return false;
		  String data;
		  if (clipboard.hasText()){                        // If clip board has text
			  CharSequence data1 = clipboard.getText();    // Get the clip
			  data = data1.toString();       
		  }else data ="";									// If no clip, set data to null
		  StringVarValues.set(theValueIndex, data);         // Return the result to user
		return true;
	}
	
	private boolean executeCLIPBOARD_PUT(){
		if (!getStringArg()) return false;					// Get the string to put into the clipboard
		if (!checkEOL()) return false;
		clipboard.setText(StringConstant);                  // Put the user expression into the clipboard
		return true;
	}
	
	// ********************************Encryption commands *****************************************

	@SuppressLint("NewApi")									// Uses value from API 8
	private boolean executeENCRYPT(){
		if (!getStringArg()) return false;					// Get the Pass Word
		String PW = StringConstant;
		if (!isNext(',')) return false;

		if (!getStringArg()) return false;					// Get the Src string
		String Src = StringConstant;
		if (!isNext(',')) return false;

		if (!getSVar()) return false;						// Get the destination Var string
		if (!checkEOL()) return false;

	    // 8-byte Salt
	    byte[] salt = {
	        (byte)0xA9, (byte)0x9B, (byte)0xC8, (byte)0x32,
	        (byte)0x56, (byte)0x35, (byte)0xE3, (byte)0x03
	    };

	    // Iteration count
	    int iterationCount = 19;
	    
	    Cipher ecipher = null;
        String Dest = "abc";

        try {
            // Create the key
            KeySpec keySpec = new PBEKeySpec(PW.toCharArray(), salt, iterationCount);
            SecretKey key = SecretKeyFactory.getInstance(
                "PBEWithMD5AndDES").generateSecret(keySpec);

            ecipher = Cipher.getInstance(key.getAlgorithm());
            // Prepare the parameter to the ciphers
            AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

            // Create the ciphers
            ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            // Encode the string into bytes using utf-8
            byte[] utf8 = Src.getBytes("UTF8");

            // Encrypt
            byte[] enc = ecipher.doFinal(utf8);

            // Encode bytes to base64 to get a string
            Dest = Base64.encodeToString(enc, Base64.NO_WRAP);
            Dest = Dest.trim();
            
        } catch (Exception e) {
        	return RunTimeError(e);
        }
        

        StringVarValues.set(theValueIndex, Dest);    // Put the encrypted string into the user variable
		return true;
	}

	@SuppressLint("NewApi")									// Uses value from API 8
	private boolean executeDECRYPT(){
		if (!getStringArg()) return false;					// Get the Pass Word
		String PW = StringConstant;
		if (!isNext(',')) return false;

		if (!getStringArg()) return false;					// Get the Src string
		String Src = StringConstant;
		if (!isNext(',')) return false;

		if (!getSVar()) return false;						// Get the destination Var string
		if (!checkEOL()) return false;

	    // 8-byte Salt
	    byte[] salt = {
	        (byte)0xA9, (byte)0x9B, (byte)0xC8, (byte)0x32,
	        (byte)0x56, (byte)0x35, (byte)0xE3, (byte)0x03
	    };

	    // Iteration count
	    int iterationCount = 19;
	    
	    Cipher dcipher = null;
        try {
            // Create the key
            KeySpec keySpec = new PBEKeySpec(PW.toCharArray(), salt, iterationCount);
            SecretKey key = SecretKeyFactory.getInstance(
                "PBEWithMD5AndDES").generateSecret(keySpec);
            dcipher = Cipher.getInstance(key.getAlgorithm());

            // Prepare the parameter to the ciphers
            AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

            // Create the ciphers
            dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
            
            
        } 
        catch (Exception e) {
        	return RunTimeError(e);
        }
        
        String Dest = "@@@@";
        
        try {
            // Decode base64 to get bytes
            byte[] dec = Base64.decode(Src, Base64.DEFAULT);
            // Decrypt
            byte[] utf8 = dcipher.doFinal(dec);
            // Encode bytes to UTF 8 to get a string
            Dest = new String(utf8, "UTF8");
        } catch (Exception e) {
        	return RunTimeError(e);
        }

        if (Dest.equals("@@@@")){
        	RunTimeError( "Invalid Password at:");
        	return false;
        }
        
        StringVarValues.set(theValueIndex, Dest);  // Put the decoded string into the user variable.

		return true;
	}
	
//************************************** Socket Commands  ******************************************

	private boolean executeSOCKET(){								// Get Socket command keyword if it is there
		return executeCommand(Socket_cmd, "Socket");
	}

	private boolean executeSocketServer(){							// Get Socket Server command keyword if it is there
		return executeCommand(SocketServer_cmd, "Socket Server");
	}

	private boolean executeSocketClient(){							// Get Socket Client command keyword if it is there
		return executeCommand(SocketClient_cmd, "Socket Client");
	}

	private boolean isServerSocketConnected() {
		return isServerSocketConnected("No current connection");
	}

	private boolean isServerSocketConnected(String msgNullSocket) {
		if (theServerSocket == null){
			RunTimeError(msgNullSocket);
			return false;
		}
		if (!theServerSocket.isConnected()){
			RunTimeError("Server Connection Disrupted");
			return false;
		}
		return true;
	}

	private boolean isClientSocketConnected() {
		if (theClientSocket == null){
			RunTimeError("Client Socket Not Opened");
			return false;
		}
		if (!theClientSocket.isConnected()){
			RunTimeError("Client Connection Disrupted");
			return false;
		}
		return true;
	}

	private boolean executeSERVER_CREATE(){
		
		if (!evalNumericExpression()) return false;							// Get the List pointer
		if (!checkEOL()) return false;

		int SocketServersServerPort = EvalNumericExpressionValue.intValue();
		try {
			newSS = new ServerSocket(SocketServersServerPort);
		} catch (Exception e) {
			return RunTimeError(e);
		}

		return true;
	}

	private boolean executeSERVER_ACCEPT(){
		
		if (newSS == null) {
			RunTimeError("Server not created");
			return false;
		}
		boolean block = true;			// Default if no "wait" parameter is to block. This preserves
										// behavior from before v01.73, when the parameter was added.
		if (evalNumericExpression()) {										// Optional "wait" parameter
			block = (EvalNumericExpressionValue != 0.0);
		}
		if (!checkEOL()) return false;
		if ((theServerSocket != null) && theServerSocket.isConnected()) return true;

		serverSocketState = STATE_LISTENING;
		serverSocketConnectThread = new SocketConnectThread();
		serverSocketConnectThread.start();
		if (block) {
			while (serverSocketState == STATE_LISTENING) { Thread.yield(); }
			if (serverSocketState != STATE_CONNECTED) { return false; }
		}
		return true;
	}

	private class SocketConnectThread extends Thread {
		public void run() {
			try {
				theServerSocket = newSS.accept();
				ServerBufferedReader = new BufferedReader(new InputStreamReader(theServerSocket.getInputStream()));
				ServerPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(theServerSocket.getOutputStream())), true);
				serverSocketState = STATE_CONNECTED;
			} catch (Exception e) {
				serverSocketState = STATE_NONE;
			} finally {
				serverSocketConnectThread = null;							// null global reference to itself
			}
		}

		@Override
		public void interrupt() {
			if (serverSocketState == STATE_LISTENING) {						// in case SERVER_DISCONNECT interrupts thread
				serverSocketState = STATE_NONE;								// change state or SERVER_STATUS will report LISTENING
			}
			super.interrupt();
		}
	}

	private boolean executeCLIENT_CONNECT(){

		if (!getStringArg()) return false;									// get the server address
		String SocketClientsServerAdr = StringConstant;
		
		if (!isNext(',')) return false;										// move to the port number
		if (!evalNumericExpression()) return false;							// get the port number
		int SocketClientsServerPort = EvalNumericExpressionValue.intValue();
		if (!checkEOL()) return false;
		
		try {
			theClientSocket = new Socket(SocketClientsServerAdr, SocketClientsServerPort);
			ClientBufferedReader = new BufferedReader(new InputStreamReader(theClientSocket.getInputStream()));
			ClientPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(theClientSocket.getOutputStream())), true);
			clientSocketState = STATE_CONNECTED;
		} catch (Exception e) {
			clientSocketState = STATE_NONE;
			return RunTimeError(e);
		}

		return true;
	}

	private boolean executeSERVER_STATUS() {

		if (!getNVar()) return false;
		if (!checkEOL()) return false;

		double status = (newSS == null)                         ? STATE_NOT_ENABLED
					  : (serverSocketState == STATE_LISTENING)  ? STATE_LISTENING
					  : (theServerSocket == null)               ? STATE_NONE
					  : theServerSocket.isConnected()           ? STATE_CONNECTED
					  :                                           STATE_NONE;
		NumericVarValues.set(theValueIndex, status);
		return true;
	}

	private boolean executeCLIENT_STATUS() {			// Transformed to UDP broadcast server

		if (!getNVar()) return false;
		if (!checkEOL()) return false;

		int mPort = 6369;						// http://sberfini.developpez.com/tutoriaux/android/broadcast-udp
		int RECEIVING_TIMEOUT_SERVER = 500;
		double status = 0;

		DatagramSocket socket = null;
		try 
		{
			socket = new DatagramSocket(mPort);
			socket.setSoTimeout(RECEIVING_TIMEOUT_SERVER);
		} 
		catch (SocketException se) 
		{
			return true;
		}
		
		byte[] receiveData = new byte[1024];
		byte[] sendData = new byte[1024];
        
		while (socket != null && !socket.isClosed())
		{
			try
			{
				DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
				socket.receive(receivePacket);
				String sentence = new String(receivePacket.getData());
				InetAddress IPAddress = receivePacket.getAddress();
				int port = receivePacket.getPort();
				if (sentence.contains("Ping"))
				{
					sendData = "Pong".getBytes();
					DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, port);
					socket.send(sendPacket);
					status = 1;
					NumericVarValues.set(theValueIndex, status);
					return true;
				}
			}
			catch (Exception e)
			{
				socket.close();
				status = 0;
				NumericVarValues.set(theValueIndex, status);
				return true;
			}
		}
		return true;
	}

	private boolean executeSERVER_CLIENT_IP(){
		if (!isServerSocketConnected("Server not connected to a client")) return false;
		return socketIP(theServerSocket);
	}

	private boolean executeCLIENT_SERVER_IP(){
		if (!isClientSocketConnected()) return false;
		return socketIP(theClientSocket);
	}

	private boolean socketIP(Socket socket){
		
		if (!getSVar()) return false;
		if (!checkEOL()) return false;

		InetAddress ia = socket.getInetAddress();
		String sia = ia.toString();

		StringVarValues.set(theValueIndex, sia);
		return true;
	}

	private boolean executeSERVER_READ_READY(){
		if (!isServerSocketConnected("No current client accepted")) return false;
		return socketReadReady(ServerBufferedReader);
	}

	private boolean executeCLIENT_READ_READY(){
		if (!isClientSocketConnected()) return false;
		return socketReadReady(ClientBufferedReader);
	}

	private boolean socketReadReady(BufferedReader reader){

		if (!getNVar()) return false;
		if (!checkEOL()) return false;

		double ready = 0 ;
		try {
			if (reader.ready()) { ready = 1; }
		} catch (IOException e) {
			return RunTimeError(e);
		}

		NumericVarValues.set(theValueIndex, ready);
		return true;
	}

	private boolean executeSERVER_READ_LINE(){
		if (!isServerSocketConnected()) return false;
		return socketReadLine(ServerBufferedReader);
	}

	private boolean executeCLIENT_READ_LINE(){
		if (!isClientSocketConnected()) return false;
		return socketReadLine(ClientBufferedReader);
	}

	private boolean socketReadLine(BufferedReader reader){

		if (!getSVar()) return false;
		if (!checkEOL()) return false;
		
		String line = null;
		try {
			line = reader.readLine();
		} catch (Exception e) {
			return RunTimeError(e);
		}
		
		if (line == null){
			line = "NULL";
		}

		StringVarValues.set(theValueIndex, line);
		return true;
	}

	private boolean executeSERVER_WRITE_LINE(){
		if (!isServerSocketConnected()) return false;
		return socketWrite(theServerSocket, ServerPrintWriter, false);
	}

	private boolean executeCLIENT_WRITE_LINE(){
		if (!isClientSocketConnected()) return false;
		return socketWrite(theClientSocket, ClientPrintWriter, false);
	}

	private boolean executeSERVER_WRITE_BYTES(){
		if (!isServerSocketConnected()) return false;
		return socketWrite(theServerSocket, ServerPrintWriter, true);
	}

	private boolean executeCLIENT_WRITE_BYTES(){
		if (!isClientSocketConnected()) return false;
		return socketWrite(theClientSocket, ClientPrintWriter, true);
	}

	private boolean socketWrite(Socket socket, PrintWriter writer, boolean byteMode){

		if (!getStringArg()) return false;
		if (!checkEOL()) return false;

		String err = null;
		if (byteMode){
			OutputStream os = null;
			try {
				os = socket.getOutputStream();
				for (int k=0; k<StringConstant.length(); ++k){
					byte bb = (byte)StringConstant.charAt(k);
					os.write(bb);
				}
			} catch (Exception e) {
				err = "Error: " + e;
			} finally {
				IOException ex = flushStream(os, null);
				ex = closeStream(os, ex);
				if (ex != null && err != null) {
					err = "Error: " + ex;
				}
			}
		} else {
			writer.println(StringConstant);
			if (writer.checkError()) {
				err = "Error writing to socket";
			}
		}
		if (err != null) {
			RunTimeError(err);
			return false;
		}
		return true;
	}

	private boolean executeSERVER_PUTFILE(){
		if (!isServerSocketConnected()) return false;
		return socketPutFile(theServerSocket);
	}

	private boolean executeCLIENT_PUTFILE(){
		if (!isClientSocketConnected()) return false;
		return socketPutFile(theClientSocket);
	}

	private boolean executeSERVER_GETFILE(){
		if (!isServerSocketConnected()) return false;
		return socketGetFile(theServerSocket);
	}

	private boolean executeCLIENT_GETFILE(){
		if (!isClientSocketConnected()) return false;
		return socketGetFile(theClientSocket);
	}

	private boolean socketPutFile(Socket socket) {

		if (!evalNumericExpression()) { return false; }						// Parm is the filenumber variable
		if (!checkEOL()) { return false; }

		int FileNumber = NumericVarValues.get(theValueIndex).intValue();
		if (!checkReadFile(FileNumber, BISlist)) { return false; }			// Check runtime errors

		Bundle FileEntry = FileTable.get(FileNumber);						// Get the bundle
		if (!checkReadByteAttributes(FileEntry)) { return false; }			// Check runtime errors

		boolean eof = FileEntry.getBoolean("eof");
		if (eof) {															// Check not EOF
			RunTimeError("Attempt to read beyond the EOF at:");
			return false;
		}
		
		BufferedInputStream bis = BISlist.get(FileEntry.getInt("stream"));
		int bufferSize = 1024*16;
		try {
			OutputStream os = socket.getOutputStream();
			DataOutputStream dos = new DataOutputStream(os);

			// Set buffer size to 16K bytes, timeout to 16 sec, time out if rate is slower than 1kb/sec
			if (!streamCopy(bis, dos, bufferSize, (long)bufferSize)) {		// Copy from file to socket
				RunTimeError("Data transmission time out.");				// Timeout
				executeSERVER_DISCONNECT();
				return false;
			}
		} catch (Exception e) {
			return RunTimeError(e);
		}
		FileEntry.putBoolean("eof", true);
		FileEntry.putBoolean("closed", true);
		return true;														// Success
	}

	private boolean socketGetFile(Socket socket) {

		if (!evalNumericExpression()) { return false; }						// Parm is the filenumber variable
		if (!checkEOL()) { return false; }

		int FileNumber = EvalNumericExpressionValue.intValue();
		if (!checkWriteFile(FileNumber, DOSlist)) { return false; }			// Check runtime errors

		Bundle FileEntry = FileTable.get(FileNumber);						// Get the bundle
		if (!checkWriteByteAttributes(FileEntry)) { return false; }			// Check runtime errors

		DataOutputStream dos = DOSlist.get(FileEntry.getInt("stream"));
		int bufferSize = 1024*512;
		try {
			InputStream is = socket.getInputStream();
			BufferedInputStream bis = new BufferedInputStream(is);
			streamCopy(bis, dos, bufferSize, 0L);							// Copy from socket to file

		} catch (Exception e) {
			return RunTimeError(e);
		}
		FileEntry.putBoolean("eof", true);
		FileEntry.putBoolean("closed", true);
		return true;														// Success
	}

	private static boolean streamCopy(BufferedInputStream bis, DataOutputStream dos, int bufferSize,
										long timeoutTime)		// time in ms, 0 means no timeout check
										throws IOException {
		IOException ex = null;
		ByteArrayBuffer byteArray = new ByteArrayBuffer(bufferSize);
		int current = 0;
		boolean timeout = false;
		long ts = SystemClock.elapsedRealtime();
		try {
			while (!timeout && ((current = bis.read()) != -1)) {			// Read the input stream
				byteArray.append((byte)current);
				if (byteArray.isFull()) {
					dos.write(byteArray.toByteArray(), 0, bufferSize);		// Write the output stream
					byteArray.clear();

					if (timeoutTime != 0) {							// If caller wants timeout checked
						long te = SystemClock.elapsedRealtime();	// If rate is too slow
						timeout = (te - ts > 16000); 				// terminate transmission
						ts = te;									// reset the start time
					}
				}
			}
			int count = byteArray.length();
			if (count > 0) {										// If there is anything in the buffer
				dos.write(byteArray.toByteArray(), 0, count);		// write it to the output stream
			}
			dos.flush();											// flush the output stream
			return !timeout;

		} catch (IOException e) {
			ex = e;
			return false;		// doesn't return
		} finally {
			ex = closeStream(dos, ex);								// close the streams
			ex = closeStream(bis, ex);
			if (ex != null) { throw ex; }
		}
	}

	private boolean executeSERVER_DISCONNECT(){
		if (!checkEOL()) return false;

		if (serverSocketConnectThread != null) {
			serverSocketConnectThread.interrupt();
			serverSocketConnectThread = null;
		}

		if (theServerSocket == null) return true;
		try {
			theServerSocket.close();
			
		} catch (Exception e) {
			return RunTimeError(e);
		} finally {
			ServerPrintWriter = null;
			ServerBufferedReader = null;
			theServerSocket = null;
			serverSocketState = STATE_NONE;
		}

		return true;
	}

	private boolean executeSERVER_CLOSE(){
		if (!checkEOL()) return false;
		if (theServerSocket != null) executeSERVER_DISCONNECT();

		try {
			if (newSS != null) newSS.close();
		} catch (Exception e) {
			return RunTimeError(e);
		} finally {
			newSS = null;
		}
		return true;
	}

	private boolean executeCLIENT_CLOSE(){
		if (!checkEOL()) return false;
		if (theClientSocket == null) return true;

		try {
			theClientSocket.close();
		} catch (Exception e) {
			return RunTimeError(e);
		} finally {
			ClientPrintWriter = null;
			ClientBufferedReader = null;
			theClientSocket = null;
			clientSocketState = STATE_NONE;
		}
		return true;
	}

	private boolean executeMYIP(){
		if (!getSVar()) return false;
		if (!checkEOL()) return false;

		String IP = "";
		try {
			for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
				NetworkInterface intf = en.nextElement();
				for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
					InetAddress inetAddress = enumIpAddr.nextElement();
					if (!(inetAddress. isLoopbackAddress() || inetAddress. isLinkLocalAddress ())) {
						IP = inetAddress.getHostAddress().toString();
						break;
					}
				}
			}
		} catch (Exception e) {
			return RunTimeError(e);
		}
		
		StringVarValues.set(theValueIndex, IP);
		return true;
	}
	
	//****************************************** TTS *******************************************

	private boolean executeTTS(){								// Get TTS command keyword if it is there
		return executeCommand(tts_cmd, "TTS");
	}

	private boolean executeTTS_INIT(){
		if (!checkEOL()) return false;
		if (theTTS != null) return true;						// done if already opened

		ttsInit = false;
		theTTS = new TextToSpeechActivity(Run.this);
		if (theTTS == null) return false;
		while (!ttsInit) {
			Thread.yield();
		}

		switch (theTTS.mStatus) {
		case TextToSpeech.SUCCESS:            break;
		case TextToSpeech.LANG_MISSING_DATA:  return RunTimeError("Language Data Missing");
		case TextToSpeech.LANG_NOT_SUPPORTED: return RunTimeError("Language Not Supported");
		default:                              return RunTimeError("TTS Init Failed. Code = " + theTTS.mStatus);
		}

		return true;
	}

	private boolean executeTTS_SPEAK(){
		if (theTTS == null) {
			return RunTimeError("Text to speech not initialized");
		}
		if (!evalStringExpression()) return false;
		String speech = StringConstant;
		boolean block = true;					// Default if no "wait" parameter is to block. This preserves
		if (isNext(',')) {						// behavior from before v01.76, when the parameter was added.
			if (!evalNumericExpression()) { return false; }		// optional "wait" flag
			block = (EvalNumericExpressionValue != 0.0);		// block if non-zero
		}
		if (!checkEOL()) return false;

		if (!ttsWaitForDone()) return false;					// wait for any previous speaking to finish

		setVolumeControlStream(AudioManager.STREAM_MUSIC);
		HashMap<String, String> params = new HashMap<String, String>();
		params.put(TextToSpeech.Engine.KEY_PARAM_STREAM, String.valueOf(AudioManager.STREAM_MUSIC));

		theTTS.mDone = false;
		theTTS.speak(speech, params);
		if (block) { ttsWaitForDone(); }						// if requested, wait for speech to complete
		return true;
	}

	private boolean executeTTS_SPEAK_TOFILE(){
		if (theTTS == null) {
			return RunTimeError("Text to speech not initialized");
		}
		if (!evalStringExpression()) return false;
		String speech = StringConstant;
		String theFileName;
		if (isNext(',')) {										// optional file name parameter
			if (!getStringArg()) { return false; }
			theFileName = StringConstant;
		} else { theFileName = "tts.wav"; }						// default file name
		if (!checkEOL()) return false;

		if (!ttsWaitForDone()) return false;					// wait for any previous speaking to finish

		HashMap<String, String> params = new HashMap<String, String>();

		theFileName = Basic.getDataPath(theFileName);
		theTTS.mDone = false;
		theTTS.speakToFile(speech, params, theFileName);
		ttsWaitForDone();										// wait for speech to complete
		return true;
	}

	private boolean executeTTS_STOP() {
		return checkEOL() && ttsWaitForDone() && ttsStop();
	}

	private boolean ttsWaitForDone() {							// wait for any outstanding speaking to finish
		while (theTTS != null) {								// because cleanup() can kill theTTS while we're not looking
			if (theTTS.mDone) break; 
			Thread.yield();
		}
		return (theTTS != null);
	}

	private boolean ttsStop() {
		if (theTTS != null) {
			theTTS.shutdown();
			theTTS = null;
		}
		return true;
	}

// **********************************************  FTP **************************************************


	public boolean executeFTP(){
		if (!GetFTPKeyWord()){ return false;}
		switch (KeyWordValue){
		case ftp_open:
			if (!executeFTP_OPEN()) return false;
			break;
		case ftp_close:
			if (!executeFTP_CLOSE()) return false;
			break;
		case ftp_dir:
			if (!executeFTP_DIR()) return false;
			break;
		case ftp_cd:
			if (!executeFTP_CD()) return false;
			break;
		case ftp_put:
			if (!executeFTP_PUT()) return false;
			break;
		case ftp_get:
			if (!executeFTP_GET()) return false;
			break;
		case ftp_delete:
			if (!executeFTP_DELETE()) return false;
			break;
		case ftp_rmdir:
			if (!executeFTP_RMDIR()) return false;
			break;
		case ftp_mkdir:
			if (!executeFTP_MKDIR()) return false;
			break;
		case ftp_rename:
			if (!executeFTP_RENAME()) return false;
			break;
		default:
		}
		return true;
	}

	private boolean GetFTPKeyWord(){						// Get a Basic keyword if it is there
															// is the current line index at a keyword?
		for (int i = 0; i < ftp_KW.length; ++i) {			// loop through the keyword list
			if (ExecutingLineBuffer.startsWith(ftp_KW[i], LineIndex)) { // if there is a match
				KeyWordValue = i;							// set the keyword number
				LineIndex += ftp_KW[i].length();			// move the line index to end of keyword
				return true;								// and report back
			}
		}
		KeyWordValue = 99;									// no keyword found
		return false;										// report fail
	}

	private boolean executeFTP_OPEN(){
		if (!getStringArg()) return false;							// URL
		String url = StringConstant;

		if (!isNext(',')) return false;
		if (!evalNumericExpression()) return false;					// Port
		int port = EvalNumericExpressionValue.intValue();

		if (!isNext(',')) return false;
		if (!getStringArg()) return false;							// User Name
		String user = StringConstant;

		if (!isNext(',')) return false;
		if (!getStringArg()) return false;							// Pass word
		String pw = StringConstant;
		if (!checkEOL()) return false;

		if (!ftpConnect( url, user, pw, port)) return false;

		FTPdir = ftpGetCurrentWorkingDirectory();
		if (FTPdir == null) return false;

		return true;
	}

		public boolean ftpConnect(String host, String username,
	            String password, int port)
		{
			try {
				mFTPClient = new FTPClient();
				// connecting to the host
				mFTPClient.connect(host, port);

				// now check the reply code, if positive mean connection success
				if (FTPReply.isPositiveCompletion(mFTPClient.getReplyCode())) {
					// login using username & password
					boolean status = mFTPClient.login(username, password);

				/* Set File Transfer Mode
				 *
				 * To avoid corruption issue you must specified a correct
				 * transfer mode, such as ASCII_FILE_TYPE, BINARY_FILE_TYPE,
				 * EBCDIC_FILE_TYPE .etc. Here, I use BINARY_FILE_TYPE
				 * for transferring text, image, and compressed files.
				 */
				 mFTPClient.setFileType(FTP.BINARY_FILE_TYPE);
				 mFTPClient.enterLocalPassiveMode();

					return status;
					}
			} catch(Exception e) {
				RunTimeError(e);
			}

			return false;
	}
		
		public String ftpGetCurrentWorkingDirectory()
		{
		    try {
		        String workingDir = mFTPClient.printWorkingDirectory();
		        return workingDir;
		    } catch(Exception e) {
		        RunTimeError(e);
		    }
		    return null;
		}
		
		private boolean executeFTP_CLOSE(){
			if (!checkEOL()) return false;
			if (FTPdir == null) return true;
			
			   try {
			        mFTPClient.logout();
			        mFTPClient.disconnect();
			        FTPdir = null;
			        return true;
			    } catch (Exception e) {
			        return RunTimeError(e);
			    }
		}

		private boolean executeFTP_DIR(){
			if (FTPdir == null) { return RunTimeError("FTP not opened"); }

			if (!getNVar()) return false;								// get the list VAR
			String dirMark = "(d)";
			if (isNext(',')) {											// optional directory marker
				if (!getStringArg()) { return false; }
				dirMark = StringConstant;
			}
			if (!checkEOL()) return false;

			ArrayList<String> theStringList = new ArrayList <String>();	// create a new user list
			int theIndex = theLists.size();
			theLists.add(theStringList);

			theListsType.add(list_is_string);							// add the type
			NumericVarValues.set(theValueIndex, (double) theIndex);		// return the list pointer

			FTPFile[] ftpFiles;
			try { ftpFiles = mFTPClient.listFiles(); }					// get the list of files
			catch (Exception e) { return RunTimeError(e); }

			for (FTPFile file : ftpFiles) {								// write file names to list var
				String name = file.getName();
				if (file.isDirectory()) { name += dirMark; }			// mark directories
				theStringList.add(name);
			}

			return true;
		}

		private boolean executeFTP_CD(){
			if (FTPdir == null) { return RunTimeError("FTP not opened"); }

			if (!getStringArg()) return false;							// new directory name
			if (!checkEOL()) return false;

			String directory_path = "/" + StringConstant;

			boolean status = false;
			try {
				status = mFTPClient.changeWorkingDirectory(directory_path);
			} catch (Exception e) {
				return RunTimeError(e);
			}
			if (!status) { return RunTimeError("Directory change failed."); }

			FTPdir = directory_path;

			return true;
		}
		
		private boolean executeFTP_GET(){
			if (FTPdir == null) { return RunTimeError("FTP not opened"); }

			if (!getStringArg()) return false;							// Source file name
			String srcFile = StringConstant;

			if (!isNext(',')) return false;
			if (!getStringArg()) return false;							// Destination file name
			String destFile = StringConstant;
			if (!checkEOL()) return false;

			destFile = Basic.getDataPath(destFile);

			return ftpDownload(srcFile, destFile);
		}

		public boolean ftpDownload(String srcFilePath, String desFilePath)
		{
			FileOutputStream desFileStream = null;
			boolean status = false;
			try {
				desFileStream = new FileOutputStream(desFilePath);
				status = mFTPClient.retrieveFile(srcFilePath, desFileStream);
				desFileStream.close();
			} catch (Exception e) {
				closeStream(desFileStream, null);
				return RunTimeError(e);
			}
			if (!status) { RunTimeError("Download error"); }
			return status;
		}

		private boolean executeFTP_PUT(){
			if (FTPdir == null) { return RunTimeError("FTP not opened"); }

			if (!getStringArg()) return false;							// Source file name
			String srcFile = StringConstant;

			if (!isNext(',')) return false;
			if (!getStringArg()) return false;							// Destination file name
			String destFile = StringConstant;
			if (!checkEOL()) return false;

			srcFile = Basic.getDataPath(srcFile);

			return ftpUpload(srcFile, destFile);
		}

		public boolean ftpUpload(String srcFilePath, String desFilePath)
		{
			FileInputStream srcFileStream = null;
			boolean status = false;
			try {
				srcFileStream = new FileInputStream(srcFilePath);
				status = mFTPClient.storeFile(desFilePath, srcFileStream);
				srcFileStream.close();
			} catch (Exception e) {
				closeStream(srcFileStream, null);
				return RunTimeError(e);
			}
			if (!status) { RunTimeError("Upload problem"); }
			return status;
		}	
		
		public boolean executeFTP_CMD(){
			if (FTPdir == null) { return RunTimeError("FTP not opened"); }

			if (!getStringArg()) return false;							// Command
			String cmd = StringConstant;

			if (!isNext(',')) return false;
			if (!getStringArg()) return false;							// String parameter
			String parms = StringConstant;

			if (!isNext(',')) return false;
			if (!getNVar()) return false;								// Numeric parameter
			if (!checkEOL()) return false;

			String[] response = null;
			try {
				response = mFTPClient.doCommandAsStrings(cmd, parms);
			} catch (Exception e) {
				return RunTimeError(e);
			}
			for (String r : response) {
				PrintShow(r);
			}
			return true;
		}
		
		private boolean executeFTP_DELETE(){
			if (FTPdir == null) { return RunTimeError("FTP not opened"); }

			if (!getStringArg()) return false;							// get the file name
			String filePath = StringConstant;
			if (!checkEOL()) return false;

			boolean status = false;
			try {
				status = mFTPClient.deleteFile(filePath);				// try to delete the file
			} catch (Exception e) {
				return RunTimeError(e);
			}
			if (!status) { RunTimeError("File not deleted"); }
			return status;
		}
		
		private boolean executeFTP_RMDIR(){
			if (FTPdir == null) { return RunTimeError("FTP not opened"); }

			if (!getStringArg()) return false;							// get the directory name
			String filePath = StringConstant;
			if (!checkEOL()) return false;

			boolean status = false;
			try {
				status = mFTPClient.removeDirectory(filePath);			// try to remove it
			} catch (Exception e) {
				return RunTimeError(e);
			}
			if (!status) { RunTimeError("Directory not deleted"); }
			return status;
		}

		private boolean executeFTP_MKDIR(){
			if (FTPdir == null) { return RunTimeError("FTP not opened"); }

			if (!getStringArg()) return false;							// get the directory name
			String filePath = StringConstant;
			if (!checkEOL()) return false;

			boolean status = false;
			try {
				status = mFTPClient.makeDirectory(filePath);
			} catch (Exception e) {
				return RunTimeError(e);
			}
			if (!status) { RunTimeError("Directory not created"); }
			return status;
		}
		
		private boolean executeFTP_RENAME(){
			if (FTPdir == null) { return RunTimeError("FTP not opened"); }

			if (!getStringArg()) return false;							// old file name
			String oldName = StringConstant;

			if (!isNext(',')) return false;
			if (!getStringArg()) return false;							// new file name
			String newName = StringConstant;
			if (!checkEOL()) return false;

			boolean status = false;
			try {
				status = mFTPClient.rename(oldName, newName);
			} catch (Exception e) {
				return RunTimeError(e);
			}
			if (!status) { RunTimeError("File not renamed"); }
			return false;
		}

// *****************************************  Bluetooth ********************************
		
		public boolean executeApp(){
			if (!GetAppKeyWord()){ return false;}

			switch (KeyWordValue){
      case app_runlist:
        StringConstant = getAllRunningActivities(); // changed by mougino
        break;
			case app_pkglist:
				StringConstant = getAllInstalledActivities(); // changed by mougino
				break;
			case app_install:
				if (!execute_app_install()) return false; // changed by mougino
				break;
			case bt_connect:
				if (!execute_BT_connect()) return false;
				break;
			case bt_device_name:
				if (!execute_BT_device_name()) return false;
				break;
			case bt_write:
				if (!execute_BT_write()) return false;
				break;
			case bt_read_ready:
				if (!execute_BT_read_ready()) return false;
				break;
			case bt_read_bytes:
				if (!execute_BT_read_bytes()) return false;
				break;
			case bt_set_uuid:
				if (!execute_BT_set_uuid()) return false;
				break;
			case bt_listen:
				if (!execute_BT_listen()) return false;
				break;
			case bt_reconnect:
				if (!execute_BT_reconnect()) return false;
				break;
			case bt_readready_resume:
				if (!execute_BT_readReady_Resume()) return false;
				break;
			case bt_disconnect:
				if (!execute_BT_disconnect()) return false;
				break;
			default:
			}
			return true;
		}

  private String getAllRunningActivities() {  // added by mougino
    final   ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    final List<RunningTaskInfo> recentTasks = activityManager.getRunningTasks(Integer.MAX_VALUE);

    String string = "** application executed (ID)\n";
    for (int i = 0; i < recentTasks.size(); i++) 
    {
      string += recentTasks.get(i).baseActivity.toShortString() + " (" + recentTasks.get(i).id + ")\n";
    }
    return string;
  }
    
  private String getAllInstalledActivities() {  // added by mougino
    ArrayList<String[]> _activitiesBuffer = new ArrayList<String[]>();
    
    final Intent intent = new Intent(Intent.ACTION_MAIN, null);
    intent.addCategory(Intent.CATEGORY_LAUNCHER);
    final List<ResolveInfo> pkgAppsList = getPackageManager().queryIntentActivities( intent, 0);

    Iterator<ResolveInfo> iterator1 = pkgAppsList.iterator();
    while (iterator1.hasNext()) {
        ResolveInfo resolveInfo = iterator1.next();
        String[] buf = new String[] {
              resolveInfo.activityInfo.packageName, 
              resolveInfo.activityInfo.name};
        _activitiesBuffer.add(buf);
    }

    String string = "** package/.MainActivity\n";
    Iterator<String[]> iterator2 = _activitiesBuffer.iterator();
    while (iterator2.hasNext()) {
        String[] buf = iterator2.next();
        string += buf[0] + "/"; // "package/"
        if (buf[1].indexOf(buf[0]) == 0) {
          string += buf[1].replace(buf[0], ""); // "package/.class"
        } else {
          string += buf[1]; // "package/differentPackage.class"
        }
        string += "\n";
    }
    return string;
  }
  
	private  boolean GetAppKeyWord(){						// Get a bluetooth command keyword if it is there
															// is the current line index at a keyword?
		for (int i = 0; i < app_KW.length; ++i) {			// loop through the keyword list
			if (ExecutingLineBuffer.startsWith(app_KW[i], LineIndex)) { // if there is a match
				KeyWordValue = i;							// set the keyword number
				LineIndex += app_KW[i].length();				// move the line index to end of keyword
				return true;								// and report back
			}
		}
		KeyWordValue = 99;									// no keyword found
		return false;										// report fail
	}

		private synchronized boolean execute_app_runlist() {
			if (!getNVar() || !checkEOL()) { return false; }
			int state = (mBluetoothAdapter == null) ? STATE_NOT_ENABLED :
						(mChatService == null)      ? STATE_NONE        : bt_state;
			NumericVarValues.set(theValueIndex, (double) state);
			return true;
		}

    
    private boolean execute_app_install(){ // changed by mougino, launches install of temp.apk
      Intent intent = new Intent(Intent.ACTION_VIEW);
      intent.setDataAndType(Uri.fromFile(new File(Basic.getDataPath("temp.apk"))),
          "application/vnd.android.package-archive");
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      startActivity(intent);
      return true;
    }
    
	  
		    public void onActivityResult(int requestCode, int resultCode, Intent data) {
		        switch (requestCode) {
		        case REQUEST_CONNECT_DEVICE_SECURE:
		            // When DeviceListActivity returns with a device to connect
		            if (resultCode == Activity.RESULT_OK) {
		                connectDevice(data, bt_Secure);
		            }
		            break;
		        case REQUEST_CONNECT_DEVICE_INSECURE:
		            // When DeviceListActivity returns with a device to connect
		            if (resultCode == Activity.RESULT_OK) {
		                connectDevice(data, false);
		            }
		            break;
		        case REQUEST_ENABLE_BT:
		            // When the request to enable Bluetooth returns
		            if (resultCode == Activity.RESULT_OK) {
		                // Bluetooth is now enabled, so set up a chat session
		            	bt_enabled = 1;
		            } else {
		                bt_enabled = -1;
		            }
		            break;
		        case VOICE_RECOGNITION_REQUEST_CODE:
		        	if (resultCode == RESULT_OK){
		    	        sttResults = new ArrayList<String>();
		        		sttResults = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
		        	}
		        	sttDone = true;
		        	break;
		        }
		    }
		    
		    public boolean handleBTMessage(Message msg) {
		        switch (msg.what) {
		            case MESSAGE_STATE_CHANGE:
		            	bt_state = msg.arg1;
		                break;
		            case MESSAGE_WRITE:
//		                byte[] writeBuf = (byte[]) msg.obj;
		                // construct a string from the buffer
//		                String writeMessage = new String(writeBuf);
		                break;
		            case MESSAGE_READ:
		                byte[] readBuf = (byte[]) msg.obj;
		                String readMessage = "";
		                // construct a string from the valid bytes in the buffer
//		                String readMessage = new String(readBuf, 0, msg.arg1);
		                try {
		                 readMessage = new String(readBuf, 0);
		                } catch (Exception e){
		                	Show ("Error: " + e);
		                }
		                readMessage = readMessage.substring(0, msg.arg1);
		                synchronized (this){
		                	if (BT_Read_Buffer.size() == 0) btReadReady = true;
		                	BT_Read_Buffer.add(readMessage);
		                }
		                break;
		            case MESSAGE_DEVICE_NAME:
		                // save the connected device's name
		                mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
		                break;
		            default:
		                return false;							// message not recognized
		        }
		        return true;									// message handled
		    }
		    
		    public void connectDevice(Intent data, boolean secure) {
		    	
		        String address = data.getExtras()
		            .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
		        btConnectDevice = null;
		        try {
		        	btConnectDevice = mBluetoothAdapter.getRemoteDevice(address);
			        if ( btConnectDevice != null) mChatService.connect(btConnectDevice, secure);
		        }
		        catch (Exception e){ 
		        	RunTimeError("Connect error: " + e);
		        }
		    }
		    
		    private boolean execute_BT_connect(){
		    	bt_Secure = true;
		    	if (evalNumericExpression()) {
		    		if (EvalNumericExpressionValue == 0) bt_Secure = false;
		    	}
		    	if (!checkEOL()) { return false; }
		    	
		        if (GRopen) {
		        	GR.startConnectBT = true;
          		  	GR.drawView.postInvalidate();									// Start GR drawing.
		        }else {
		        	Intent serverIntent = null;
		        	serverIntent = new Intent(this, DeviceListActivity.class);
		        	if (serverIntent == null){
		        		RunTimeError("Error selecting device");
		        		return false;
		        	}
		        	if (bt_Secure) {
		        		startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_SECURE);
		        	}else {
		        		startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_INSECURE);
		        	}
		        }
		    		return true;
		    }
		    
		    private boolean execute_BT_disconnect(){
		    	if (!checkEOL()) { return false; }
		    	mChatService.disconnect();
		    	return true;
		    }
		    
		    private boolean execute_BT_reconnect(){
		    	if (!checkEOL()) { return false; }
		    	if (btConnectDevice == null) {
		    		RunTimeError("Not previously connected");
		    		return false;
		    	}
		    	mChatService.connect(btConnectDevice, bt_Secure);
		    	return true;
		    }
		    
		    private boolean execute_BT_listen(){
		    	if (!checkEOL()) { return false; }
//		    	mChatService.start();
		    	return true;
		    }
		    
		    private boolean execute_BT_device_name(){
		        if (bt_state != STATE_CONNECTED) {
		            RunTimeError("Bluetooth not connected");
		            return false;
		        }
		    	
		    	if (!getSVar() || !checkEOL()) return false;
		    	StringVarValues.set(theValueIndex, mConnectedDeviceName);
		    	return true;
		    }
		    
		    private boolean execute_BT_write(){
		        if (bt_state != STATE_CONNECTED) {
		            RunTimeError("Bluetooth not connected");
		            return true;                                // Deliberately not making error fatal
		        }
		        
				if (!buildPrintLine("", "\n")) return false;	// build up the text line in StringConstant

		        // Check that there's actually something to send
		        if (StringConstant.length() > 0) {
		            // Get the message bytes and tell the BluetoothChatService to write
		        	byte[] send = new byte[StringConstant.length()];
					for (int k=0; k<StringConstant.length(); ++k){
						send[k] = (byte)StringConstant.charAt(k);
					}

		            mChatService.write(send);
		        }
		    	return true;
		    }

		    private boolean execute_BT_read_ready(){
		    	if (!getNVar() || !checkEOL()) return false;
		    	double d = 0;
		    	if (bt_state == STATE_CONNECTED) {
		    		synchronized (this){
		    		d = (double)BT_Read_Buffer.size();
		    		}
		    	}
		    	NumericVarValues.set(theValueIndex, d );
		    	return true;
		    	
		    }
		    
		    private boolean execute_BT_readReady_Resume(){
		    	if (interruptResume == -1) {
		    		RunTimeError("No Bluetooth Read Ready Interrupt");
		    		return false;	    		
		    	}
		    	return doResume();
		    }
		    
		    private boolean execute_BT_read_bytes(){
		    	
		        if (bt_state != STATE_CONNECTED) {
		            RunTimeError("Bluetooth not connected");
		            return false;
		        }
		        
		        String msg = "";
		        if (bt_state == STATE_CONNECTED) {
		        	synchronized (this){
		        		int index = BT_Read_Buffer.size();
		        		if (index > 0 ){
		        			msg = BT_Read_Buffer.get(0);
		        			BT_Read_Buffer.remove(0);
		        		}
		        	}
		        }
		        
		        if (!getSVar() || !checkEOL()) return false;
		        StringVarValues.set(theValueIndex, msg);
		        
		    	return true;
		    }
		    
		    private boolean execute_BT_set_uuid(){
		    	if (!evalStringExpression() || !checkEOL()) return false;
		    	UUID MY_UUID_SECURE = UUID.fromString(StringConstant);
		    	UUID MY_UUID_INSECURE = UUID.fromString(StringConstant);
		    	return true;
		    }

//    ***********************************  Superuser and System ***************************

	private boolean executeSU(boolean isSU) {	// SU command (isSU true) or system comand (isSU false)
		for (Command c : SU_cmd) {								// loop through the command list
			if (ExecutingLineBuffer.startsWith(c.name, LineIndex)) { // if there is a match
				LineIndex += c.name.length();					// move the line index to end of keyword
				if (SUprocess == null) {
					if (c.name.equals("open")) this.isSU = isSU;
					else return RunTimeError((isSU ? "Superuser" : "System shell") + " not opened");
				}
				return c.run();									// run the function and report back
			}
		}
		RunTimeError("Unknown " + (isSU ? "SU" : "System") + " command");
		return false;											// no keyword found
	}

		    private boolean execute_SU_open(){
		    	if (!checkEOL()) return false;
		    	if (SUprocess != null) return true;
		    	SU_ReadBuffer = new ArrayList<String>();    		// Initialize buffer
		    	
		    	try
		        {
		            if (isSU) SUprocess = Runtime.getRuntime().exec("su");				// Request Superuser
		            else {
		            	File dir = new File(Basic.getFilePath());
		            	if (!dir.exists()) { dir.mkdirs(); }
		            	SUprocess = Runtime.getRuntime().exec("sh", null, dir);			// Open ordinary shell
		            }
		            SUoutputStream = new DataOutputStream(SUprocess.getOutputStream()); // Open the output stream
		            SUinputStream = new DataInputStream(SUprocess.getInputStream());    // Open the input stream
		        }
		        catch (Exception e)
		        {
		            RunTimeError("SU Exception: " + e);
		            return false;
		        }
		        theSUReader = new SUReader(SUinputStream, SU_ReadBuffer);
		        theSUReader.start();

		    	return true;
		    	
		    }
		    
		    private boolean execute_SU_write(){
		    	if (!evalStringExpression()) return false;
		    	if (!checkEOL()) return false;
		    	String command = StringConstant;
		    	try {
		    		SUoutputStream.writeBytes(command + "\n");  // Write the command followed by new line character
		    		SUoutputStream.flush();
		    	}
		    	catch (Exception e){
		            RunTimeError("SU Exception: " + e);
		            return false;	    		
		    	}

		    	return true;
		    }
		    
		    private boolean execute_SU_read_ready(){
		    	if (!getNVar()) return false;
		    	synchronized (SU_ReadBuffer) {
		    		NumericVarValues.set(theValueIndex, (double) SU_ReadBuffer.size() ); // Return buffer size
		    	}
		    	return true;
		    }

		    private boolean execute_SU_read_line(){
		        String msg = "";
		        synchronized (SU_ReadBuffer){
		        	int index = SU_ReadBuffer.size();
		        	if (index > 0 ){
		        		msg = SU_ReadBuffer.get(0);    // Read the first item in the buffer
		        		SU_ReadBuffer.remove(0);       // and then remove it.
		        	}
		        }
		        if (!getSVar()) return false;
		        StringVarValues.set(theValueIndex, msg);
		    	return true;
		    }
		    
		    private boolean execute_SU_close(){
		    	theSUReader.stop();
		    	SUprocess.destroy();

		    	SUoutputStream = null;
		    	SUinputStream = null;
		    	SU_ReadBuffer = null;
		    	theSUReader = null;
		    	SUprocess = null;

		    	return true;
		    }
		    
/****************************************** CONSOLE Commands  ************************************/

	private boolean executeCONSOLE() {							// Get Console command keyword if it is there
		return executeCommand(Console_cmd, "Console");
	}

	private boolean executeCONSOLE_TITLE() {					// Set the console title string
		String title;
		if (isEOL()) {
			title = null;										// Use default
		} else {
			if (!getStringArg() || !checkEOL()) return false;	// Get new title
			title = StringConstant;
		}
		sendMessage(MESSAGE_CONSOLE_TITLE, title);				// Signal UI to update its title
		return true;
	}

	private boolean executeCONSOLE_LINE_COUNT(){
		if (!getNVar()) return false;							// variable to hold the number of lines
		if (!checkEOL()) return false;

		int count = output.size();								// number of lines written to console
		NumericVarValues.set(theValueIndex, (double)count);
		return true;
	}

	private boolean executeCONSOLE_LINE_TEXT(){
		if (!evalNumericExpression()) return false;				// line number to read
		int lineNum = EvalNumericExpressionValue.intValue();
		if (!isNext(',') || !getSVar() || !checkEOL()) return false; // variable for line content

		if (--lineNum < 0) {									// convert from 1-based user index to 0-based Java index
			return RunTimeError("Line number must be >= 1");
		}
		int max = output.size();								// number of lines written to console
		String lineText = (lineNum < max) ? output.get(lineNum) : "";
		StringVarValues.set(theValueIndex, lineText);
		return true;
	}

	private boolean executeCONSOLE_LINE_TOUCHED(){
		if (!getNVar()) return false; 							// variable for last line number touched
		int lineVarIndex = theValueIndex;
		int longTouchVarIndex = -1;
		if (isNext(',')) {
			if (!getNVar()) return false;						// optional variable indicating short or long touch
			longTouchVarIndex = theValueIndex;
		}
		if (!checkEOL()) return false;

		NumericVarValues.set(lineVarIndex, (double)TouchedConsoleLine);
		if (longTouchVarIndex != -1) {
			double isLongTouch = ConsoleLongTouch ? 1 : 0;
			NumericVarValues.set(longTouchVarIndex, isLongTouch);
		}
		return true;
	}

	private boolean executeCONSOLE_DUMP(){

		if (!getStringArg() || !checkEOL()) { return false; }	// Only parameter is the filename
		String theFileName = StringConstant;

		theBackground.checkpointProgress();						// allow any pending Console activity to complete
		while (ProgressPending) { Thread.yield(); }				// wait for checkpointProgress semaphore to clear

		File file = new File(Basic.getDataPath(theFileName));
		try {
			file.createNewFile();
		} catch (Exception e) {
			return RunTimeError(e);
		}
		if (!file.exists() || !file.canWrite()) {
			return RunTimeError("Problem opening " + theFileName);
		}

		FileWriter writer = null;
		try {
			writer = new FileWriter(file, false);				// open the filewriter for the SD Card
			for (String line : output) {
				writer.write(line + "\n");
			}
			writer.flush();
			writer.close();
		} catch (Exception e) {
			return RunTimeError(e);
		}
		// Log.d(LOGTAG, CLASSTAG + " executeCONSOLE_DUMP: file " + theFileName + " written");

		return true;
	}

	  private boolean executeCONSOLE_FRONT(){
		  Basic.theProgramRunner.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT );
		  startActivity(Basic.theProgramRunner);
		  GRFront = false;

		  return true;
	  }

	  private boolean executeCONSOLE_LINE_NEW(){
		  Show("@@A");
		  return true;
	  }

	  private boolean executeCONSOLE_LINE_CHAR(){
		  if (!evalStringExpression()) return false;
		  char c = StringConstant.charAt(0);
		  Show("@@B" + c);
		  return true;
	  }
	  
/******************************************** SOUND POOL ******************************************/
	  
		public boolean executeSP(){
			
			if (!GetSPKeyWord()){ return false;}
			
			if (theSoundPool == null &&
				KeyWordValue != sp_open){
				RunTimeError("SoundPool not opened");
				return false;
			}

			switch (KeyWordValue){
			case sp_open:
				if (!execute_SP_open()) return false;
				break;
			case sp_load:
				if (!execute_SP_load()) return false;
				break;
			case sp_play:
				if (!execute_SP_play()) return false;
				break;
			case sp_stop:
				if (!execute_SP_stop()) return false;
				break;
			case sp_unload:
				if (!execute_SP_unload()) return false;
				break;
			case sp_pause:
				if (!execute_SP_pause()) return false;
				break;
			case sp_resume:
				if (!execute_SP_resume()) return false;
				break;
			case sp_release:
				if (!execute_SP_release()) return false;
				break;
			case sp_setvolume:
				if (!execute_SP_setvolume()) return false;
				break;
			case sp_setpriority:
				if (!execute_SP_setpriority()) return false;
				break;
			case sp_setloop:
				if (!execute_SP_setloop()) return false;
				break;
			case sp_setrate:
				if (!execute_SP_setrate()) return false;
				break;
					
			default:
			}
			return true;
		}

	private  boolean GetSPKeyWord(){						// Get a soundpool keyword if it is there
															// is the current line index at a keyword?
		for (int i = 0; i < sp_KW.length; ++i) {			// loop through the keyword list
			if (ExecutingLineBuffer.startsWith(sp_KW[i], LineIndex)) { // if there is a match
				KeyWordValue = i;							// set the keyword number
				LineIndex += sp_KW[i].length();				// move the line index to end of keyword
				return true;								// and report back
			}
		}
		KeyWordValue = 99;									// no keyword found
		return false;										// report fail
	}

	  private boolean execute_SP_open(){
		  
		  if (!evalNumericExpression()) return false;
		  if (!checkEOL()) return false;
		  int SP_max = EvalNumericExpressionValue.intValue();
		  if (SP_max <=0 ){
			  RunTimeError("Stream count must be > 0");
			  return false;			  
		  }
//		  this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
		  theSoundPool = new SoundPool(SP_max,  AudioManager.STREAM_MUSIC, 0);
		  
		  return true;
	  }

	private boolean execute_SP_load(){
		if (!getNVar()) return false;
		int savedIndex = theValueIndex;
		if (!isNext(',')) { return false; }

		if (!getStringArg()) return false;								// Get the file path
		if (!checkEOL()) return false;
		String fileName = StringConstant;								// The filename as given by the user
		String fn = Basic.getDataPath(fileName);

		int SoundID = theSoundPool.load(fn, 1);
		if (SoundID == 0) {												// if file does not exist
			if (Basic.isAPK) {											// and this is a user APK
				int resID = Basic.getRawResourceID(fileName);			// try to load the file from a raw resource
				if (resID != 0) {
					SoundID = theSoundPool.load(Basic.BasicContext, resID, 1);
				} else {												// try to load the file from assets
					AssetFileDescriptor afd = null;
					try {
						String assetPath = Basic.getAppFilePath(Basic.DATA_DIR, fileName);
						afd = getAssets().openFd(assetPath);
						SoundID = theSoundPool.load(afd, 1);
					}
					catch (IOException ex) { }
					finally {
						if (afd != null) { try { afd.close(); } catch (IOException e) { } }
					}
				}
				if (SoundID == 0) { return false; }
			}
		}
		NumericVarValues.set(savedIndex, (double) SoundID );
		return true;
	}

	  private boolean execute_SP_play(){
		  
		  if (!getNVar()) return false;                        			// Stream return variable
		  int savedIndex = theValueIndex;

			if (!isNext(',')) return false;								// Sound ID
			if (!evalNumericExpression()) return false;
			int soundID = EvalNumericExpressionValue.intValue();
			
			if (!isNext(',')) return false;								// Left Volume
			if (!evalNumericExpression()) return false;
			float leftVolume = EvalNumericExpressionValue.floatValue();
			
			if (!isNext(',')) return false;								// Right Volume
			if (!evalNumericExpression()) return false;
			float rightVolume = EvalNumericExpressionValue.floatValue();
			
			if (!isNext(',')) return false;								// Priority
			if (!evalNumericExpression()) return false;
			int priority = EvalNumericExpressionValue.intValue();

			if (!isNext(',')) return false;								// Loop
			if (!evalNumericExpression()) return false;
			int loop = EvalNumericExpressionValue.intValue();
			
			if (!isNext(',')) return false;								// Rate
			if (!evalNumericExpression()) return false;
			float rate = EvalNumericExpressionValue.floatValue();
			if (!checkEOL()) return false;
			
			if (leftVolume < 0 || leftVolume >= 1.0){
				RunTimeError("Left volume out of range");
				return false;
			}

			if (rightVolume < 0 || rightVolume >= 1.0){
				RunTimeError("Right volume out of range");
				return false;
			}
			
			if (priority < 0 ){
				RunTimeError("Priority less than zero");
				return false;
			}

			if (rate < 0.5 || rate > 1.8  ){
				RunTimeError("Rate out of range");
				return false;
			}
			
			
		int streamID = theSoundPool.play(soundID, leftVolume, rightVolume, priority, loop, rate);
			
		  	NumericVarValues.set(savedIndex, (double) streamID );
			
		  return true;
	  }
	  
	  private boolean execute_SP_stop(){
			if (!evalNumericExpression()) return false;
			if (!checkEOL()) return false;
			int streamID = EvalNumericExpressionValue.intValue();
			theSoundPool.stop(streamID);

		  return true;
	  }
	  
	  private boolean execute_SP_unload(){
			if (!evalNumericExpression()) return false;
			if (!checkEOL()) return false;
			int soundID = EvalNumericExpressionValue.intValue();
			theSoundPool.unload(soundID);

		  return true;
	  }

	@SuppressLint("NewApi")									// Uses value from API 8
	private boolean execute_SP_pause(){
		if (!evalNumericExpression()) return false;
		if (!checkEOL()) return false;

		int streamID = EvalNumericExpressionValue.intValue();
		if (streamID == 0 ) theSoundPool.autoPause();
		else theSoundPool.pause(streamID);

		return true;
	}

	@SuppressLint("NewApi")									// Uses value from API 8
	private boolean execute_SP_resume(){
		if (!evalNumericExpression()) return false;
		if (!checkEOL()) return false;

		int streamID = EvalNumericExpressionValue.intValue();
		if (streamID == 0 ) theSoundPool.autoResume();
		else theSoundPool.resume(streamID);

		return true;
	}

	  private boolean execute_SP_release(){
		  if (!checkEOL()) return false;
		  theSoundPool.release();
		  theSoundPool = null;
		  return true;
	  }
	  
	  private boolean execute_SP_setvolume(){
		  
			if (!evalNumericExpression()) return false;
			int streamID = EvalNumericExpressionValue.intValue();
			
			if (!isNext(',')) return false;								// Left Volume
			if (!evalNumericExpression()) return false;
			float leftVolume = EvalNumericExpressionValue.floatValue();
			
			if (!isNext(',')) return false;								// Right Volume
			if (!evalNumericExpression()) return false;
			float rightVolume = EvalNumericExpressionValue.floatValue();
			if (!checkEOL()) return false;
			
			if (leftVolume < 0 || leftVolume >= 1.0 ){
				return RunTimeError("Left volume out of range");
			}

			if (rightVolume < 0 || rightVolume >= 1.0){
				return RunTimeError("Right volume out of range");
			}

			theSoundPool.setVolume(streamID, leftVolume, rightVolume);


		  return true;
	  }
	  
	  private boolean execute_SP_setpriority(){
			if (!evalNumericExpression()) return false;
			int streamID = EvalNumericExpressionValue.intValue();
			
			if (!isNext(',')) return false;								// Priority
			if (!evalNumericExpression()) return false;
			int priority = EvalNumericExpressionValue.intValue();
			if (!checkEOL()) return false;
			
			if (priority < 0 ){
				RunTimeError("Priority less than zero");
				return false;
			}
			
			theSoundPool.setPriority(streamID, priority);

		  return true;
	  }
	  
	  private boolean execute_SP_setloop(){
			if (!evalNumericExpression()) return false;
			int streamID = EvalNumericExpressionValue.intValue();
			
			if (!isNext(',')) return false;								// Loop value
			if (!evalNumericExpression()) return false;
			int loop = EvalNumericExpressionValue.intValue();
			if (!checkEOL()) return false;
			
			theSoundPool.setLoop(streamID, loop);

		  return true;
	  }
	  

	  private boolean execute_SP_setrate(){
			if (!evalNumericExpression()) return false;
			int streamID = EvalNumericExpressionValue.intValue();
			
			if (!isNext(',')) return false;								// Rate
			if (!evalNumericExpression()) return false;
			float rate = EvalNumericExpressionValue.floatValue();
			if (!checkEOL()) return false;
			
			if (rate < 0.5 || rate > 1.8){
				RunTimeError("Rate out of range");
				return false;
			}
			
			theSoundPool.setRate(streamID, rate);
		  return true;
	  }
	  
	  //*********************************** Get my phone number ************************
	  
	  private boolean execute_my_phone_number(){
		  
		  if (!getSVar()) return false;
		  if (!checkEOL()) return false;
		  
		  TelephonyManager mTelephonyMgr;
	        mTelephonyMgr = (TelephonyManager)
	            getSystemService(Context.TELEPHONY_SERVICE);
	      String pn =  mTelephonyMgr.getLine1Number();
	      
	      if (pn == null) pn = "Get phone number failed." ;
	      
	      StringVarValues.set(theValueIndex, pn);
	        
		  return true;
	  }
	  
	  // ********************************* Headset *****************************************
	  
	  private boolean executeHEADSET(){
		  
		if (!getNVar()) return false;
		NumericVarValues.set(theValueIndex, (double) headsetState) ;
		  
			char c = ExecutingLineBuffer.charAt(LineIndex);					// Loop value					
			if ( c != ',') return false;
			++LineIndex;

		if (!getSVar()) return false;
	    StringVarValues.set(theValueIndex, headsetName);
	      
			c = ExecutingLineBuffer.charAt(LineIndex);					// Loop value					
			if ( c != ',') return false;
			++LineIndex;
			
		if (!getNVar()) return false;
		NumericVarValues.set(theValueIndex, (double) headsetMic) ;
		  
		  return true;
	  }
	  
	  // ******************************** SMS ***************************************
	  
	  private boolean executeSMS_SEND(){
		  
		  if (!evalStringExpression()) return false;
		  String number = StringConstant;

			char c = ExecutingLineBuffer.charAt(LineIndex);					// Loop value					
			if ( c != ',') return false;
			++LineIndex;

		  if (!evalStringExpression()) return false;
		  String msg = StringConstant;
		  
		  SmsManager sm = android.telephony.SmsManager.getDefault();
		  try {
			  sm.sendTextMessage(number, null, msg, null, null);
		  } catch (Exception e) {
			  return RunTimeError(e);
		  }
		  
		  return true;
	  }
	  
	  private boolean executeSMS_RCV_INIT(){
		  
		  registerReceiver(receiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));
		  smsRcvBuffer = new ArrayList <String>();
		  return true;
	  }
	  
	  private boolean executeSMS_RCV_NEXT(){
		  if (smsRcvBuffer == null){
			  RunTimeError("SMS.RCV.INIT not executed)");
			  return false;
		  }
		  
		  if (!getSVar()) return false;
		  
		  if (smsRcvBuffer.size() == 0){
			  StringVarValues.set(theValueIndex, "@");
			  return true;
		  }
		  
		  StringVarValues.set(theValueIndex, smsRcvBuffer.get(0));
		  smsRcvBuffer.remove(0);
		  
		  return true;
	  }
	  
	  private BroadcastReceiver receiver = new BroadcastReceiver () {

          @Override
          public void onReceive(Context arg0, Intent arg1) {
              Bundle bundle = arg1.getExtras();
              SmsMessage[] recievedMsgs = null;
              String str = "";
              if (bundle != null)
              {
                  Object[] pdus = (Object[]) bundle.get("pdus");
                  recievedMsgs = new SmsMessage[pdus.length];
                  for (int i=0; i < recievedMsgs.length; ++i)
                  {
                  	  recievedMsgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
                      str += "SMS from " + recievedMsgs[i].getOriginatingAddress()+ " :" + recievedMsgs[i].getMessageBody().toString();
                      if (smsRcvBuffer != null)
                    	  smsRcvBuffer.add(str);
                  }
              }
          }
      };
	  
	  //******************************* Phone Call ***********************************
	  
 	  private boolean executePHONE_CALL(){
		  if (!evalStringExpression()) return false;
		  String number = "tel:" + StringConstant;

		  Intent callIntent = new Intent(Intent.ACTION_CALL);  
		  callIntent.setData(Uri.parse(number));
		  callIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // this will make such that when user returns to your app, your app is displayed, instead of the email app.

		  try {
			  startActivityForResult(callIntent, BASIC_GENERAL_INTENT);  
		  } catch (Exception e){
			  return RunTimeError(e);
		  }
		  return true;
	  }

	private boolean executePHONE_RCV_INIT(){
		if (phoneRcvInited) return true;

		phoneRcvInited = true;

		mTM = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
		mTM.listen(PSL, PhoneStateListener.LISTEN_CALL_STATE);
 
		return true;
	}

		PhoneStateListener PSL = new PhoneStateListener() {
 			public void onCallStateChanged(int state,  String incomingNumber) {
 				phoneState = state;
 				if (phoneState == TelephonyManager.CALL_STATE_RINGING) phoneNumber = incomingNumber;
 			}
 		};

 	  
 	  private boolean executePHONE_RCV_NEXT(){
 		 if (!phoneRcvInited) {
 			 RunTimeError("phone.rcv.init not executed");
 			 return false;
 		 }
 		 
 		 
 		 int callState = mTM.getCallState();
 		 if (callState == TelephonyManager.CALL_STATE_IDLE) phoneNumber = "";
 		 
 		  if (!getNVar()) return false;
 		  int stateIndex = theValueIndex;
 		  
			char c = ExecutingLineBuffer.charAt(LineIndex);									
			if ( c != ',') return false;
			++LineIndex;
			
			if (!getSVar()) return false;
			int numberIndex = theValueIndex;
			
			
			NumericVarValues.set(stateIndex, (double)callState);
			StringVarValues.set(numberIndex, phoneNumber);
			
			
			return true;
 	  }
 	  
	  //********************************  EMAIL  ************************************
	  
	  private boolean executeEMAIL_SEND(){
		  
		  if (!evalStringExpression()) return false;
		  String recipiant = "mailto:" + StringConstant;

			char c = ExecutingLineBuffer.charAt(LineIndex);									
			if ( c != ',') return false;
			++LineIndex;

		  if (!evalStringExpression()) return false;
		  String subject = StringConstant;

			c = ExecutingLineBuffer.charAt(LineIndex);										
			if ( c != ',') return false;
			++LineIndex;

		  if (!evalStringExpression()) return false;
		  String body = StringConstant;
		  
		  Intent intent = new Intent(Intent.ACTION_SENDTO); // it's not ACTION_SEND
		  intent.setType("text/plain");
		  intent.putExtra(Intent.EXTRA_SUBJECT, subject);
		  intent.putExtra(Intent.EXTRA_TEXT, body);
		  intent.setData(Uri.parse(recipiant)); // or just "mailto:" for blank
		  intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // this will make such that when user returns to your app, your app is displayed, instead of the email app.
		  try {
			  startActivityForResult(intent, BASIC_GENERAL_INTENT);
		  }catch (Exception e){
			  return RunTimeError(e);
		  }
		  
		  return true;
	  }
	  
	  // *********************************** html ********************************************
	  
		public boolean executehtml(){
			
			if (htmlOpening){
				while (Web.aWebView == null) Thread.yield();
				htmlOpening = false;
			}
			
			
			if (!GethtmlKeyWord()){ return false;}
			
			if (KeyWordValue == html_open ||            // Allow open and get.datalink if not opened
			    KeyWordValue == html_get_datalink){}
			    else
			
			    	if (htmlIntent == null ||
			    		Web.aWebView == null)
			    	{
			    		if (KeyWordValue != html_close) {      // If already closed and user asking
			    			RunTimeError("html not opened");   // to close, then don't give an error
			    			return false;
			    		}
			    		else return true;
			    	}

			switch (KeyWordValue){
			case html_open:
				if (!execute_html_open()) return false;
				break;
			case html_load_url:
				if (!execute_html_load_url()) return false;
				break;
			case html_load_string:
				if (!execute_html_load_string()) return false;
				break;
			case html_get_datalink:
				if (!execute_html_get_datalink()) return false;
				break;
			case html_close:
				if (!execute_html_close()) return false;
				break;
			case html_go_back:
				if (!execute_html_go_back()) return false;
				break;
			case html_go_forward:
				if (!execute_html_go_forward()) return false;
				break;
			case html_clear_cache:
				if (!execute_html_clear_cache()) return false;
				break;
			case html_clear_history:
				if (!execute_html_clear_history()) return false;
				break;
			case html_post:
				if (!execute_html_post()) return false;
				break;
			default:
			}
			return true;
		}

	private boolean GethtmlKeyWord(){						// Get n HTML keyword if it is there
															// is the current line index at a keyword?
		for (int i = 0; i < html_KW.length; ++i) {			// loop through the keyword list
			if (ExecutingLineBuffer.startsWith(html_KW[i], LineIndex)) { // if there is a match
				KeyWordValue = i;							// set the keyword number
				LineIndex += html_KW[i].length();			// move the line index to end of keyword
				return true;								// and report back
			}
		}
		KeyWordValue = 99;									// no keyword found
		return false;										// report fail
	}

	  private boolean execute_html_open(){
		  if (Web.aWebView != null) {
			return RunTimeError("HTML previously open and not closed");
		  }
		  
		  ShowStatusBar = 0;
		  if (evalNumericExpression()){
			  ShowStatusBar = EvalNumericExpressionValue.intValue();
		  }
		  if (!checkEOL()) return false;

          htmlIntent= new Intent(this, Web.class);         // Intent variable used to tell if opened
          Web.aWebView = null;							   // Will be set int Web.java
          htmlData_Buffer = new ArrayList<String>();	   // Initialize the datalink buffer
		sendMessage(MESSAGE_HTML_OPEN);						// Start Web View in UI thread.
          htmlOpening = true;
          
          return true;
	  }

	private String getURL(String path) {					// build a URL for a file or directory in the file system or assets
		String urlPath = Basic.getDataPath(path);			// if path is null, get full path of default data directory
															// if non-null, get full path of a file in the data directory
		if (!new File(urlPath).exists() && Basic.isAPK) {	// if file or dir does not exist in file system, and this is an APK
			String assetPath = Basic.getAppFilePath(Basic.DATA_DIR, path);	// then look for it in assets
			String type = getAssetType(assetPath);			// type is null if asset not found
			String expType = (path == null) ? "d" : "f";	// are we looking for a directory or a file?
			if ((type != null) && type.equals(expType)) {	// if asset exists and is the expected type, use its path
				urlPath = "/android_asset/" + assetPath;
			}
		}													// else use the file system path
		return "file://" + urlPath;							// make the path into a URL
	}

	private boolean execute_html_load_url(){				// Load an internet url
		if (!getStringArg() || !checkEOL()) return false;

		String urlString = StringConstant;
		String protocolName = urlString.substring(0,4);
		if (!protocolName.equals("http") && !protocolName.equals("java") && !protocolName.equals("file")) {
		urlString = getURL(urlString);						// Get URL with full path to file in file system or assets.
															// If neither file nor asset exists,
															// path points to non-existent file system file.
		}
		sendMessage(MESSAGE_LOAD_URL, urlString);
		return true;
	}

	private boolean execute_html_load_string(){				// Load an html string
		if (!getStringArg() || !checkEOL()) return false;
		String baseURL = getURL(null) + File.separatorChar;	// baseURL is default data directory in file system or assets.
															// If directory does not exist in either file system or assets,
															// path points to non-existent file system directory.
		String[] data = { baseURL, StringConstant };
		sendMessage(MESSAGE_LOAD_STRING, data);
		return true;
	}

	private boolean execute_html_get_datalink(){			// Gets a data sring from datalink queue

		if (!getSVar()) return false;						// The string return variable
		if (!checkEOL()) return false;

		String data = "";
		if (htmlData_Buffer != null)
			if (htmlData_Buffer.size() > 0)					// If the buffer is not empty
				data = htmlData_Buffer.remove(0);			// get the oldest entry and remove it from the buffer

		StringVarValues.set(theValueIndex, data);			// return the data to the user

		if (Web.aWebView == null) return true;				// if already closed, return now
															// else check to see if we should close
//		if (data.startsWith("FOR:")) return execute_html_close();	// if Form, close the html
		if (data.startsWith("ERR:")) return execute_html_close();	// if error, close the html

		return true;
	}

	private boolean execute_html_go_back(){
		if (!checkEOL()) return false;
//		Web.aWebView.goBack();
		sendMessage(MESSAGE_GO_BACK);
		return true;
	}

	private boolean execute_html_go_forward(){
		if (!checkEOL()) return false;
//		Web.aWebView.goForward();
		sendMessage(MESSAGE_GO_FORWARD);
		return true;
	}

	private boolean execute_html_clear_cache(){
		if (!checkEOL()) return false;
//		Web.aWebView.clearCache();
		sendMessage(MESSAGE_CLEAR_CACHE);
		return true;
	}

	private boolean execute_html_clear_history(){
		if (!checkEOL()) return false;
//		Web.aWebView.clearHistory();
		sendMessage(MESSAGE_CLEAR_HISTORY);
		return true;
	}

	  private boolean execute_html_close(){							// Close the html
		  if (!checkEOL()) return false;
		  if (Web.aWebView != null) Web.aWebView.webClose();        // if it is open
		  while (Web.aWebView != null) Thread.yield();              // wait for the close signal
		  htmlIntent = null;										// Indicate not open
		  return true;
	  }

	private boolean execute_html_post(){
		if (!getStringArg()) return false;
		String url = StringConstant;

		if (!isNext(',')) return false; 
		if (!evalNumericExpression()) return false;
		if (!checkEOL()) return false;

		int theListIndex = EvalNumericExpressionValue.intValue();
		if (theListIndex < 1 || theListIndex >= theLists.size()) {
			return RunTimeError("Invalid list pointer");
		}
		if (theListsType.get(theListIndex) == list_is_numeric){
			return RunTimeError("List must be of string type.");
		}
		List<String> thisList = theLists.get(theListIndex);
		int r = thisList.size() % 2;
		if (r != 0) {
			return RunTimeError("List must have even number of elements");
		}

		StringBuilder sb = new StringBuilder();
		int i = 0;
		while (i < thisList.size()) {
			sb.append(thisList.get(i++)).append('=');
			sb.append(thisList.get(i++)).append('&');
		}
		String[] params = { url, sb.substring(0, sb.length() - 1) };

		sendMessage(MESSAGE_POST, params);
		return true;
	}

	public boolean handleHtmlMessage(Message msg) {
		if ((msg.what == MESSAGE_HTML_OPEN) && (htmlIntent != null)) {
			startActivityForResult(htmlIntent, BASIC_GENERAL_INTENT);
		} else if (Web.aWebView != null) {
			switch (msg.what) {
			case MESSAGE_GO_BACK:		Web.aWebView.goBack();		break;
			case MESSAGE_GO_FORWARD:	Web.aWebView.goForward();	break;
			case MESSAGE_CLEAR_CACHE:	Web.aWebView.clearCache();	break;
			case MESSAGE_CLEAR_HISTORY:	Web.aWebView.clearHistory();break;
			case MESSAGE_LOAD_URL:
				String url = (String)msg.obj;
				Web.aWebView.webLoadUrl(url);
				break;
			case MESSAGE_LOAD_STRING:
				String[] data = (String[])msg.obj;
				Web.aWebView.webLoadString(data[0], data[1]);	// baseURL and HTML.Load.String argument
				break;
			case MESSAGE_POST:
				String[] params = (String[])msg.obj;
				Web.aWebView.webPost(params[0], params[1]);		// URL and data for "POST" request
				break;
			default:
				return false;									// message not recognized
			}
		}
		return true;											// message handled
	}

	//************************************** Run Command ************************************

	private boolean executeRUN(){

		if (!getStringArg()) { return false; }								// get program filename
		String fileName = StringConstant;

		String data = "";
		if (isNext(',')) {													// optional
			if (!getStringArg()) { return false; }							// parameter to pass to program
			data = StringConstant;
		}
		if (!checkEOL()) { return false; }

		String path = Basic.getFilePath(Basic.SOURCE_DIR, fileName);
		boolean exists = false;
		if (!Basic.isAPK) { exists = new File(path).exists(); }				// standard BASIC can only RUN a file
		else if (Basic.getRawResourceID(fileName) != 0) { exists = true; }	// APK can run resource
		else try {															// or asset
			String assetPath = Basic.getAppFilePath(Basic.SOURCE_DIR, fileName);
			getAssets().openFd(assetPath); 									// exception if asset does not exist
			exists = true;
		} catch (IOException ex) { }
		if (!exists) {														// error if the program does not exist
			return RunTimeError(fileName + " not found");
		}

		Bundle bb = new Bundle();
		bb.putString("fn", fileName);										// without the path
		bb.putString("data", data);
		bb.putBoolean("RUN", true);											// tell AutoRun this is a RUN command

		Intent intent = new Intent(this, AutoRun.class);
		intent.putExtras(bb);

		startActivity(intent);
		finish();

		return true;
	}

	  //**************************************** Empty Program Command *********************************
	  
	  private boolean executeEMPTY_PROGRAM(){
		  Show("Nothing to execute.");
		  Stop = true;
		  return true;
	  }
	  
	  // *************************************** Notify Command ***********************************
	  
	  private boolean executeNOTIFY(){
		  
		  int NOTIFICATION_ID = 1;    // These two constants are without meaning in this application
		  int REQUEST_CODE = 2;

		if (!getStringArg()) return false;
		String title = StringConstant;

		if (!isNext(',')) return false;
		if (!getStringArg()) return false;
		String subtitle = StringConstant;

		if (!isNext(',')) return false;
		if (!getStringArg()) return false;
		String msg = StringConstant;

		if (!isNext(',')) return false;
		if (!evalNumericExpression()) return false;				// logical expression: wait flag
		double wait = EvalNumericExpressionValue;

		  Notified = false;
		  
		  NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		  Notification notification = new Notification(R.drawable.icon, msg, System.currentTimeMillis());

		  // The PendingIntent will launch activity if the user selects this notification
		  PendingIntent contentIntent = PendingIntent.getActivity(this, REQUEST_CODE, new Intent(this, HandleNotify.class), 0);

		  notification.setLatestEventInfo(this, title, subtitle, contentIntent);
		  notification.flags = Notification.FLAG_AUTO_CANCEL;
		  
		  manager.notify(NOTIFICATION_ID, notification);
		  
		  if (wait != 0) 
			  while (!Notified) Thread.yield();
		  
		  return true;
	  }
	  
	  // ***********************************  SWAP Command *********************************************
	  
	  private boolean executeSWAP(){
		  
		  if (getNVar()) {
			  int aIndex = theValueIndex;
			  double aValue = NumericVarValues.get(aIndex);
			  
			  if (ExecutingLineBuffer.charAt(LineIndex)!=','){SyntaxError(); return false;} 
			  ++LineIndex;
			  
			  if (!getNVar()) return false;
			  int bIndex = theValueIndex;
			  double bValue = NumericVarValues.get(bIndex);
			  
			  NumericVarValues.set(aIndex, bValue);
			  NumericVarValues.set(bIndex, aValue);
			  
			  return true;
			  
		  }
		  else if (!getSVar()) return false;
		  
		  int aIndex = theValueIndex;
		  String aValue = StringVarValues.get(aIndex);
		  
		  if (ExecutingLineBuffer.charAt(LineIndex)!=','){SyntaxError(); return false;} 
		  ++LineIndex;
		  
		  if (!getSVar()) return false;
		  int bIndex = theValueIndex;
		  String bValue = StringVarValues.get(bIndex);
		  
		  StringVarValues.set(aIndex, bValue);
		  StringVarValues.set(bIndex, aValue);
		  
		  
		  return true;
	  }
	  
	  // ****************************** Speach to text Command ******************************
	  
	  private boolean executeSTT_LISTEN(){
		  
	        PackageManager pm = getPackageManager();
	        List<ResolveInfo> activities = pm.queryIntentActivities(
	                new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
	        if (activities.size() == 0) {
	        	RunTimeError("Recognizer not present");
	        	return false;
	        }
	        
	        sttListening = true;
	        sttDone = false;
	        if (GRopen) GR.doSTT = true;
	        else startVoiceRecognitionActivity();
	        return true;
	  }
	        
	  private boolean executeSTT_RESULTS(){
		  if (!sttListening) {
			  RunTimeError("STT_LISTEN not executed.");
			  return false;
		  }
	        while (!sttDone) Thread.yield();
	        sttListening = false;
	        
			int theIndex = theLists.size();
			if (sttResults == null) {
				sttResults = new ArrayList <String>();
				sttResults.add("Recognition Cancelled");
			}
			theLists.add(sttResults);
			theListsType.add(list_is_string);
			
			if (!getNVar()) return false;
			NumericVarValues.set(theValueIndex, (double) theIndex);   		// Return the list pointer    

		  return true;
	  }
	  
	    private void startVoiceRecognitionActivity() {
	        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
	        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
	                RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
	        intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "BASIC! Speech To Text");
	        startActivityForResult(intent, VOICE_RECOGNITION_REQUEST_CODE);
	    }
	    
	  // ******************************* Timer commands ***************************************
	    
	private boolean executeTIMER(){											// Get Timer command keyword if it is there
		return executeCommand(Timer_cmd, "Timer");
	}

	   private boolean executeTIMER_SET(){
		   if (theTimer != null) {
			   RunTimeError("Previous Timer Not Cleared");
			   return false;
		   }
		   
		   if (OnTimerLine == 0) {
			   RunTimeError("No OnTimer: Label");
			   return false;
		   }
		   
		   
		   if (!evalNumericExpression()) return false;
		   long interval = EvalNumericExpressionValue.intValue();
		   if (interval < 100) {
			   RunTimeError("Interval Must Be >= 100");
			   return false;
		   }
		   
		   if (!checkEOL()) return false;
		   
		   TimerTask tt = new TimerTask() {
			    public void run() {
			        // Delegate to the same runnable each time.
			        toRunRepeatedly.run();
			    }
			};

		   timerExpired= false;
		   timerStarting = true;
		   theTimer = new Timer();
		   theTimer.scheduleAtFixedRate (tt, 100, interval);	   
		   
	    	return true;
	    }
	   
		
		   Runnable toRunRepeatedly = new Runnable() {
			    public void run() {
			    	if (timerStarting)
			    		timerStarting = false;
			    	else
			    		timerExpired= true;
			    }
		   };


	    
	   private boolean executeTIMER_CLEAR(){
		   if (!checkEOL()) return false;
		   
		   if (theTimer != null) {
			   theTimer.cancel();
			   theTimer = null;
		   }
	    	return true;
	   }
	   
	   private boolean executeTIMER_RESUME(){
		   if (interruptResume == -1) {
			   RunTimeError("No timer interrupt to reseume");
			   return false; 
		   }
	       return doResume();
	    }
	   
	   // ***************************** Home Command **************************************
	   
	   private boolean executeHOME(){
		   moveTaskToBack(true);
		   return true;
	   }

	   private boolean executeBACKGROUND_RESUME() {
		   if (interruptResume == -1) {
			   RunTimeError("No background state change");
			   return false;
		   }
		   return doResume();
	   }

} // End of Run
